基於 HTML5 Canvas 的可互動旋鈕元件
阿新 • • 發佈:2019-07-29
前言
此次的 Demo 效果如下:
Demo 連結:https://hightopo.com/demo/comp-knob/
整體思路
- 元件引數
- 繪製旋鈕
- 繪製刻度
- 繪製指標
- 繪製標尺
- 繪製文字
1.元件引數
以下是下文會使用到的部分變數,在此先貼出來
var origin, // 原點 percent, // 顯示刻度佔總刻度的百分比 partAngle, // 每個刻度所佔的角度 startAngle, //刻度起始的角度 calibrationPoints, // 每個刻度的資訊 pointer, // 指標的資訊 scaleLine, // 標尺的資訊 calibrationColors // 刻度漸變色
2.繪製旋鈕
這裡主要就使用了 canvas api 中的 arc() 和 createRadialGradient() 。
主要程式碼:
g.beginPath(); var ringRadial = g.createRadialGradient(origin.x, origin.y, 0, origin.x, origin.y, ringRadio); ringRadial.addColorStop(0, ht.Default.brighter(ringColor, 20)); ringRadial.addColorStop(0.95, ht.Default.brighter(ringColor, 40)); ringRadial.addColorStop(1, ht.Default.darker(ringColor, 20)); var borderRadial = g.createRadialGradient(origin.x, origin.y, ringRadio - ringBorderWidth / 2, origin.x, origin.y, ringRadio + ringBorderWidth / 2); borderRadial.addColorStop(0, ht.Default.brighter(ringBorderColor, 2)); borderRadial.addColorStop(0.5, ht.Default.brighter(ringBorderColor, 4)); borderRadial.addColorStop(1, ht.Default.darker(ringBorderColor, 4)); g.fillStyle = ringRadial; g.lineWidth = ringBorderWidth; g.strokeStyle = borderRadial; g.arc(origin.x, origin.y, ringRadio, 0, 2 * Math.PI); g.closePath(); g.fill(); g.stroke();
效果圖:
3.繪製刻度
這裡繪製每個刻度採用的是繪製路徑的方法,所以聲明瞭一個變數 calibrationPoints 用來存放每個刻度的起始點座標,根據配置的引數去計算 calibrationPoints 的資訊。
主要程式碼:
var calibrationPoints = []; for (var i = 0; i < calibrationCount + 1; i++) { var point = { startx: origin.x + (ringRadio + ringBorderWidth + 0 * calibrationHeight) * Math.cos(startAngle - i * partAngle), starty: origin.y - (ringRadio + ringBorderWidth + 0 * calibrationHeight) * Math.sin(startAngle - i * partAngle), endx: origin.x + (ringRadio + ringBorderWidth + 1 * calibrationHeight) * Math.cos(startAngle - i * partAngle), endy: origin.y - (ringRadio + ringBorderWidth + 1 * calibrationHeight) * Math.sin(startAngle - i * partAngle) }; if (i <= (calibrationCount * percent) && percent > 0) { point.show = true; } else { point.show = false; } calibrationPoints.push(point); }
有了每個刻度的資訊後,接下來就開始繪製刻度。首先繪製了所有的刻度,然後替換畫筆顏色為高亮色,再繪製當前需要高亮的刻度。
主要程式碼:
calibrationPoints.forEach(function (i, index) { g.beginPath(); if (calibrationColorWheelShow) { calibrationBrightColor = calibrationColors[index]; } g.lineWidth = 1.2 * calibrationWidth; g.strokeStyle = i.show ? calibrationBrightColor : ht.Default.brighter(calibrationDarkColor, 10); g.moveTo(i.startx, i.starty); g.lineTo(i.endx, i.endy); g.closePath(); g.stroke(); }) calibrationPoints.forEach(function (i, index) { g.beginPath(); if (calibrationColorWheelShow) { calibrationBrightColor = calibrationColors[index]; } g.lineWidth = calibrationWidth; g.strokeStyle = i.show ? calibrationBrightColor : calibrationDarkColor; g.moveTo(i.startx, i.starty); g.lineTo(i.endx, i.endy); g.closePath(); g.stroke(); })
效果圖:
考慮到一種高亮顏色太單調,於是加了個色輪。思路:給每個刻度都添加了顏色的標識。
主要程式碼:
if (calibrationColorWheelShow) { // 顯示刻度色輪 var colors = []; calibrationColorWheel.forEach(function (i) { colors.push(ht.Default.toColorData(i)) }) // 把顏色值轉換成rgb方式,設定多少秒改變完成,每次改變多少值,計算需要多少次 // ,比如rba(x,y,z)到rgb(a,b,c),假設需要100次,那麼每次設定 // rgb((a-x)/100+x,(b-y)/100+y,(c-z)/100+z) var count = Math.ceil(calibrationCount / (calibrationColorWheel.length - 1)); // 漸變次數 calibrationColors = []; for (var i = 0; i < colors.length - 1; i++) { for (var j = 1; j <= count; j++) { var item = 'rgb(' + Math.round((colors[i + 1][0] - colors[i][0]) / j + colors[i][0]) + ',' + Math.round((colors[i + 1][1] - colors[i][1]) / j + colors[i][1]) + ',' + Math.round((colors[i + 1][2] - colors[i][2]) / j + colors[i][2]) + ')'; calibrationColors.push(item) } } }
效果圖:
4.繪製指標
這個主要是根據三角函式去計算相對圓心的偏移角度,按照當前值和刻度最大值的比例來計算偏移量,然後換算成對應的座標。
主要程式碼:
pointer = { x: origin.x + (ringRadio - pointerRadio - ringBorderWidth) * Math.cos(startAngle - Math.PI * 2 * calibrationPercent * percent), y: origin.y - (ringRadio - pointerRadio - ringBorderWidth) * Math.sin(startAngle - Math.PI * 2 * calibrationPercent * percent), r: pointerRadio, color: percent > 0 ? calibrationBrightColor : calibrationDarkColor, show: true, } if (pointerShow) { g.beginPath(); g.fillStyle = pointer.color; g.arc(pointer.x, pointer.y, pointer.r, 0, Math.PI * 2); g.closePath(); g.fill(); }
效果圖:
5.繪製標尺
計算標尺角度的演算法同指針。
主要程式碼:
scaleLine = { startx: origin.x, starty: origin.y, endx: origin.x + (ringRadio + ringBorderWidth + 2 * calibrationHeight) * Math.cos(startAngle - Math.PI * 2 * calibrationPercent * percent), endy: origin.y - (ringRadio + ringBorderWidth + 2 * calibrationHeight) * Math.sin(startAngle - Math.PI * 2 * calibrationPercent * percent), color: percent > 0 ? calibrationBrightColor : calibrationDarkColor, show: scaleLineShow, } if (scaleLine) { g.beginPath(); g.strokeStyle = 'red'; g.setLineDash([1, 2]); g.lineWidth = 0.5 * calibrationWidth; g.moveTo(scaleLine.startx, scaleLine.starty); g.lineTo(scaleLine.endx, scaleLine.endy); g.closePath(); g.stroke(); }
效果圖:
6.繪製文字
主要程式碼:
if (labelShow) { var text = ht.Default.getTextSize(font, value); g.fillStyle = labelColor; g.font = font; g.fillText(value.toFixed(2), labelDot.x, labelDot.y); }
效果圖:
到這就完成了基本的旋鈕元件,下面繼續做一些細節上的優化。
主要程式碼:
var backgroundRadial = g.createRadialGradient(x + 0.5 * width, y + 0.2 * height, 0, x + 0.5 * width, y + 0.2 * height, Math.sqrt(Math.pow(width / 2, 2) + Math.pow(height, 2))); backgroundRadial.addColorStop(0, 'rgba(220,220,220,1)'); backgroundRadial.addColorStop(1, backgroundColor); g.fillStyle = backgroundRadial; g.fillRect(x, y, width, height); g.beginPath(); var ringRadial = g.createRadialGradient(origin.x, origin.y - ringRadio / 2, 0, origin.x, origin.y - ringRadio / 2, 1.5 * ringRadio); ringRadial.addColorStop(0, ht.Default.brighter(ringColor, 40)); // ringRadial.addColorStop(0.25, ht.Default.brighter(ringColor, 40)); ringRadial.addColorStop(1, ht.Default.darker(ringColor, 20)); var borderRadial = g.createRadialGradient(origin.x, origin.y, ringRadio - ringBorderWidth / 2, origin.x, origin.y, ringRadio + ringBorderWidth / 2); borderRadial.addColorStop(0, ht.Default.brighter(ringBorderColor, 2)); borderRadial.addColorStop(0.5, ht.Default.brighter(ringBorderColor, 4)); borderRadial.addColorStop(1, ht.Default.darker(ringBorderColor, 4)); g.fillStyle = ringRadial; g.lineWidth = ringBorderWidth; g.strokeStyle = borderRadial; g.arc(origin.x, origin.y, ringRadio, 0, 2 * Math.PI); g.closePath(); g.fill(); g.shadowBlur = 20; g.shadowColor = shadowColor; g.shadowOffsetY = ringBorderWidth; g.stroke(); g.shadowBlur = 0; g.shadowOffsetY = 0;
效果圖:
&n