1. 程式人生 > >canvas - 圓圈內 hover效果

canvas - 圓圈內 hover效果

return oct page store () itl listen mas all

技術分享

各種計算還挺繁瑣的, 關鍵點在角度的計算, 根據鼠標位置, 利用atan(y/x) 得到反正切值 , 角度 (tan輸入的是r和x圍成的那個角,輸出的是y/x。反tan就是輸入y/x輸出角。)

<!DOCTYPE html>
<html>
<body>

<canvas id="canvas" width="800" height="950" style="border:1px solid #d3d3d3;margin-top:10px">
Your browser does not support the HTML5 canvas tag.
</canvas>
<script>

const canvas = document.getElementById(‘canvas‘);
const ctx = canvas.getContext(‘2d‘);
const PI = Math.PI;

const number = 4;//畫4個外圓;
const reduceRadio = 15;//遞減半徑;

const maxRadius = 80;//最大半徑
const minRadio = maxRadius-number*reduceRadio;

const translateX = 100;//canvas移動位置
const translateY = 100;

//canvas移動位置
ctx.translate(translateX,translateY);

//畫所有圓
function drawAllOuterCircle(number,reduceRadio,maxRadius){

//畫虛線
ctx.setLineDash([2,2]);

for(let i=0;i<number;i++){

ctx.beginPath();

//半徑遞減
ctx.arc(0,0, maxRadius-i*reduceRadio, 0, 2*PI);

if(i+n == 4){
ctx.strokeStyle = ‘red‘;
}else{
ctx.strokeStyle = ‘black‘;
}

ctx.stroke();
ctx.closePath();

}

//重置
ctx.restore();

//最裏面小圓
ctx.beginPath();
ctx.arc(0,0,maxRadius-number*reduceRadio,0,2*PI);
ctx.fillStyle="red";
ctx.fill();
ctx.closePath();

ctx.restore();
}

drawAllOuterCircle(number,reduceRadio,maxRadius);

//畫分割直線
const lineNum = 8;//分割線條數
const minAngel = 2*PI/lineNum;//最小角度

function drawSplitLine(lineNum,maxRadius){
for(let i=0;i<lineNum;i++){
ctx.setLineDash([]);

ctx.beginPath();
ctx.moveTo(0,0);
ctx.lineTo(0,maxRadius);
ctx.strokeStyle = ‘black‘;
ctx.stroke();

//旋轉
ctx.rotate(2*PI/lineNum);
ctx.closePath();
}
}

drawSplitLine(lineNum,maxRadius);

//畫陰影部分
function drawShadowPanel(ceilMouseRadio,floorMouseRadio,ceilMouseAngel,floorMouseAngel){

//清屏
ctx.beginPath();
ctx.moveTo(0,0);
ctx.arc(0,0,ceilMouseRadio+reduceRadio,0,2*PI);
ctx.fillStyle = "white";
ctx.fill();
ctx.closePath();

//調試: 先畫一個大的綠色扇形
ctx.beginPath();
ctx.moveTo(0,0);
ctx.arc(0,0,ceilMouseRadio,floorMouseAngel,ceilMouseAngel);
ctx.fillStyle = "green";
ctx.fill();
ctx.closePath();

//再畫一個小的白色扇形 覆蓋 大的扇形, 視覺上形成一個弧形
ctx.beginPath();
ctx.moveTo(0,0);
ctx.arc(0,0,floorMouseRadio,floorMouseAngel,ceilMouseAngel);
ctx.fillStyle = "white";
ctx.fill();
ctx.closePath();
}

const maxN = (maxRadius-minRadio)/reduceRadio;
var n;


//獲取ceilMouseRadio,floorMouseAngel,ceilMouseAngel

//獲取canvas 的 位移
const canvasX = canvas.getBoundingClientRect().left + document.documentElement.scrollLeft +translateX;
const canvasY = canvas.getBoundingClientRect().top + document.documentElement.scrollTop +translateY;

function getMouseDetail(mouseX,mouseY){

//利用勾股定理 算出鼠標 離 canvas原點的距離
let mouseRadio = Math.sqrt( Math.pow(mouseX-canvasX, 2) + Math.pow(mouseY-canvasY, 2));

//因為 cavas原點已經移動到 100 100, 判斷鼠標是否在圓圈內
if(mouseRadio>maxRadius||mouseRadio<=minRadio){
return {ceilMouseRadio:0,floorMouseRadio:0,floorMouseAngel:0,ceilMouseAngel:0};
}

//計算鼠標半徑和最小半徑的比例,畫陰影塊的大小半徑 reduceRadio:15
n = Math.ceil((mouseRadio-minRadio)/reduceRadio);

let ceilMouseRadio = minRadio+n*reduceRadio;
let floorMouseRadio = minRadio+(n-1)*reduceRadio;

//角度
let mouseAngel;

if(mouseX-canvasX<0){
//計算當前鼠標 相對原點的的弧度值 , x軸 順時針

// 比如 Math.atan(1)/Math.PI*180 是 45度
mouseAngel = Math.atan((mouseY-canvasY)/(mouseX-canvasX)) + PI;
}else{
mouseAngel = Math.atan((mouseY-canvasY)/(mouseX-canvasX));
}

// 計算當前鼠標 有多少個象限
let m = Math.ceil(mouseAngel/minAngel);

let floorMouseAngel = (m-1)*minAngel;
let ceilMouseAngel = m*minAngel;

return {ceilMouseRadio,floorMouseRadio,floorMouseAngel,ceilMouseAngel};
}

//監聽事件畫圖
canvas.addEventListener(‘mousemove‘, (e)=>{

let mouseX = e.pageX;
let mouseY = e.pageY;

getMouseDetail(mouseX,mouseY);

let mouseDetail = getMouseDetail(mouseX,mouseY);
let {ceilMouseRadio,floorMouseRadio,floorMouseAngel,ceilMouseAngel} = mouseDetail;


if(ceilMouseRadio){
//先畫陰影,
drawShadowPanel(ceilMouseRadio,floorMouseRadio,ceilMouseAngel,floorMouseAngel);
//再畫圓圈
drawAllOuterCircle(number,reduceRadio,maxRadius);
drawSplitLine(lineNum,maxRadius);
}
});

</script>

</body>
</html>

鏈接

canvas - 圓圈內 hover效果