Preface
This time Demo has the following effects:
Demo Links: https://hightopo.com/demo/comp-knob/
Overall thinking
- Component parameters
- Draw knob
- Draw scale
- Draw Pointer
- Draw Ruler
- Draw Text
1. Component parameters
Here are some of the variables you'll use below, which you'll paste here first
var origin, // origin percent, // Display the percentage of scale to total scale partAngle, // Angle of each scale startAngle, //Angle of scale start calibrationPoints, // Information for each scale pointer, // Pointer information scaleLine, // Ruler information calibrationColors // Scale gradient
2. Draw knobs
The main uses here are arc() and createRadialGradient() in the canvas api.
Main Code:
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();
Design sketch:
3. Draw scale
Here, each scale is drawn using the method of drawing a path, so a variable calibrationPoints is declared to store the coordinates of the starting point of each scale, and the information about calibrationPoints is calculated based on the parameters configured.
Main Code:
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); }
With the information for each scale, the next step is to draw the scale.First all the scales are drawn, then the brush color is replaced by the highlight color, and then the scales that currently need to be highlighted are drawn.
Main Code:
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(); })
Design sketch:
Considering that a highlight color is too monotonous, a color wheel is added.Idea: Add a color to each scale.
Main Code:
if (calibrationColorWheelShow) { // Show scale wheel var colors = []; calibrationColorWheel.forEach(function (i) { colors.push(ht.Default.toColorData(i)) }) // Convert color values to rgb How to set how many seconds the changes are completed, how many values are changed each time, and how many times the changes are calculated // ,such as rba(x,y,z)reach rgb(a,b,c),Assuming 100 times, set each time // rgb((a-x)/100+x,(b-y)/100+y,(c-z)/100+z) var count = Math.ceil(calibrationCount / (calibrationColorWheel.length - 1)); // Gradient Number 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) } } }
Design sketch:
4. Draw Pointer
This is mainly based on the trigonometric function to calculate the offset angle from the center of the circle, the offset is calculated according to the ratio of the current value to the maximum value of the scale, and then converted to the corresponding coordinates.
Main Code:
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(); }
Design sketch:
5. Draw rulers
The algorithm for calculating the angle of a ruler is the same as the pointer.
Main Code:
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(); }
Design sketch:
6. Draw Text
Main Code:
if (labelShow) { var text = ht.Default.getTextSize(font, value); g.fillStyle = labelColor; g.font = font; g.fillText(value.toFixed(2), labelDot.x, labelDot.y); }
Design sketch:
Now that you have finished the basic knob components, let's continue with some detailed optimizations.
Main Code:
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;
Design sketch: