這次的 Demo 效果以下:html
Demo 連接:hightopo.com/demo/comp-k…node
如下是下文會使用到的部分變量,在此先貼出來算法
var origin, // 原點
percent, // 顯示刻度佔總刻度的百分比
partAngle, // 每一個刻度所佔的角度
startAngle, //刻度起始的角度
calibrationPoints, // 每一個刻度的信息
pointer, // 指針的信息
scaleLine, // 標尺的信息
calibrationColors // 刻度漸變色複製代碼
這裏主要就使用了 canvas api 中的 arc() 和 createRadialGradient() 。canvas
主要代碼:api
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();複製代碼
效果圖:bash
這裏繪製每一個刻度採用的是繪製路徑的方法,因此聲明瞭一個變量 calibrationPoints 用來存放每一個刻度的起始點座標,根據配置的參數去計算 calibrationPoints 的信息。ide
首先根據參數 calibrationPercent 計算第一個刻度的起始角度 startAngle ,而後根絕 calibrationCount 的值去計算每一個刻度所佔用的角度 partAngle ,最後根據三角函數和相應的角度,轉化爲對應的座標。函數
主要代碼:優化
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();
})複製代碼
效果圖:
考慮到一種高亮顏色太單調,因而加了個色輪。思路:給每一個刻度都添加了顏色的標識。
每一個刻度的顏色計算方法:把顏色值轉換成 rgb 方式,設定多少秒改變完成,每次改變多少值,計算須要多少次,好比 rba(x,y,z) 到 rgb(a,b,c),假設須要 100 次,那麼每次設定 rgb((a - x) / 100 + x, (b - y) / 100 + y, (c - z) / 100 + z) 。
主要代碼:
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)
}
}
}複製代碼
效果圖:
這個主要是根據三角函數去計算相對圓心的偏移角度,按照當前值和刻度最大值的比例來計算偏移量,而後換算成對應的座標。
主要代碼:
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();
}複製代碼
效果圖:
計算標尺角度的算法同指針。
主要代碼:
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();
}複製代碼
效果圖:
這裏主要的就是肯定文本所要繪製的位置,而後根據 ht.Default.getTextSize() 來獲取文本長度,方便設置文本居中。
主要代碼:
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;複製代碼
效果圖:
以上就是繪製好了一張靜態圖,最後就只要再加上一些交互效果就能夠了。
這裏我採用的是 HT for Web 的矢量來實現。可參考 → 戳這
監聽 onUp 和 onDraw 事件。
onUp:
當鼠標擡起時,獲取當前旋鈕顯示的值,而後四捨五入,取其最近的刻度校準,使用 ht.Default.startAnim() 添加動畫效果。
onDraw:
根據當前鼠標停留的位置,以旋鈕原點爲參照點,根據三角函數來計算指針和起始刻度的夾角 A ,計算 A 佔總刻度的百分比 p ,而後設置當前值爲 max * p 。
最後使用 HT 實現:
var gv = new ht.graph.GraphView();
dm = gv.dm();
dm.a('pannable', true);
dm.a('zoomable', true);
dm.a('rectSelectable', true);
ht.Default.setCompType('knob',func); //註冊組件
ht.Default.setImage('iconKnob', data); //註冊圖片
var node = new ht.Node();
node.setImage('iconKnob');
node.s('2d.movable', false);
dm.add(node);
gv.fitContent();
gv.addToDOM();
window.addEventListener(
'resize',
function(e) {
gv.invalidate();
gv.fitContent();
},
false
);複製代碼