Interactive knob component based on HTML5 Canvas

Keywords: PHP

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:

Posted by Maugrim_The_Reaper on Sun, 28 Jul 2019 17:52:01 -0700