Нарисуйте диск прогресса с холстом

внешний интерфейс Canvas

Когда я работал, я получил дизайн-чертеж пользовательского интерфейса, который имеет эффект, аналогичный диску прогресса (я не знаю, каково официальное название). . Это напоминает мне полотно, которое давно заброшено. После подтверждения эффекта давайте поиграем с холстом. .

Анализ пользовательского интерфейса

Схема пользовательского интерфейса представлена ​​на рисунке:

image

Далее, согласно иерархическому анализу, выделяют несколько частей:

  1. Полый круг (вид сзади индикатора выполнения)
  2. Полый круг (индикатор выполнения)
  3. Полый круг (граница заполненного круга)
  4. заполненный круг
  5. текст (прогресс)
  6. текст (вторая строка)

Подтверждено, что нужно нарисовать как минимум 6 частей детали.

начать рисовать

Холст понять

В соответствии с требованиями вышеуказанных уровней могут использоваться следующие API.

// 线宽
ctx.lineWidth
// 用于指定结束线帽的样式
ctx.lineCap
// 绘制渐变色
ctx.createLinearGradient
// 指定线条绘图颜色
ctx.strokeStyle
// 指定填充绘图颜色
ctx.fillStyle
// 创建一个圆
ctx.arc()
// 绘制文字
ctx.fillText()
// 指定文字字样
ctx.font
// 指定文字位置
ctx.textAlign

Инструкции по рисованию круга на холсте

то естьctx.arc(x, y, redius, startAngle, endAngle, counterclockwise)Этот метод может создать круг и получить 6 параметров, которые являются координатой x центра круга, координатой y центра круга, радиусом круга, начальным радианом, конечным радианом, нужно ли рисовать против часовой стрелки

Угол=180°×радиан÷π, радиан=угол×π÷180°

Полный круг: 360° × π ÷ 180° = 2 × π

Подводя итог, начиная с 0 до Math.PI * 2 это круг

Путь рисования окружности показан на рисунке

image

Для получения более подробной информации см.:W3school

Сначала объявите холст

// HTML部分
<canvas id="canvas"></canvas>
// JS部分
let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');
canvas.width = Math.ceil(300 / 1920 * window.innerWidth);
canvas.height = Math.ceil(300 / 1920 * window.innerWidth);

создать класс

class Annulus {

    constructor(obj={}){
        this.size = 100;
        this.lineWidth = 10;
        this.location = obj.location || {x: canvas.width/2, y: canvas.height/2};
    }

    // 绘制进度条底层
    drawBg(){}
    // 绘制进度条
    drawCircleLay(){}
    // 绘制中心的大圆
    drawCenterCircle(){}
    // 绘制大圆边缘的边
    drawCenterBorderCircle(){}
    // 绘制进度文字
    drawTextPercent(){}
    // 绘制进度下面的文字 
    drawTextName(){}
    // 定义动画
    animate(){
        this.drawBg();
        this.drawCircleLay();
        this.drawCenterCircle();
        this.drawCenterBorderCircle();
        this.drawTextPercent();
        this.drawTextName();
    }
    // 执行
    run(){
        this.animate();
    }
}

фон индикатора выполнения

drawBg(){
    // 绘制背景圈
    ctx.beginPath();
    ctx.strokeStyle = '#2d4264';
    ctx.lineWidth = 10;
    ctx.lineCap = "round";
    ctx.arc(this.location.x, this.location.y, 100, Math.PI*0.75, Math.PI*2.25, false);
    ctx.stroke();
}

индикатор

drawCircleLay(){
    // 绘制进度条
    ctx.beginPath();
    var gradient = ctx.createLinearGradient(0, this.linearLocation().start, 0, this.linearLocation().end);
    gradient.addColorStop(0, '#0f6cd9');
    gradient.addColorStop(1, '#05a6da');
    ctx.strokeStyle = gradient;
    ctx.lineWidth = this.lineWidth;
    ctx.lineCap = "round";
    ctx.arc(this.location.x, this.location.y, this.size, Math.PI*0.75, Math.PI*2.25, false);
    ctx.stroke();
}

linearLocation(){
    // 设定渐变背影的起始结束点
    let start = this.location.y - ((this.size-15)*2 + this.lineWidth)/2;
    let end = start + (this.size-15)*2 + this.lineWidth
    return {start: start, end: end}   
}

В настоящее время это в основном то же самое, что и фоновый круг, разница в том, что здесь используется цвет градиента, который рисуется первым, а затем добавляются переменные изменения.

центральный круг

drawCenterCircle(){
    // 绘制中心圆
    ctx.beginPath();
    var gradient = ctx.createLinearGradient(0, this.linearLocation().start, 0, this.linearLocation().end);
    gradient.addColorStop(0, '#39a8ce');
    gradient.addColorStop(1, '#5647c9');
    ctx.fillStyle = gradient;
    ctx.arc(this.location.x, this.location.y, this.size-15, 0, Math.PI*2, false);
    ctx.fill();
}

сторона центрального круга

drawCenterBorderCircle(){
    // 绘制中心圆周边的那圈
    ctx.beginPath();
    ctx.strokeStyle = 'rgba(0,0,0,0.3)';
    ctx.lineWidth = 10;
    ctx.arc(this.location.x, this.location.y, this.size-20, 0, Math.PI*2, false);
    ctx.stroke();
}

прогресс рисования

drawTextPercent(percent){
    // 绘制进度文字
    ctx.beginPath();
    ctx.font = '31px Arial';
    ctx.textAlign="center";
    ctx.fillStyle="#192f47";
    ctx.fillText(`100%`, this.location.x, this.location.y);
    ctx.stroke();
}

Текст под ходом рисования

drawTextName(){
    // 绘制二级文字
    ctx.beginPath();
    ctx.font = '14px "Microsoft YaHei"';
    ctx.textAlign="center";
    ctx.fillStyle="#192f47";
    ctx.fillText('text', this.location.x, this.location.y+25);
    ctx.stroke();
}

заставить его двигаться

Далее мы можем сделать прогресс бар перемещения.
Есть несколько точек для занятий спортом.

  1. скоростьspeed
  2. Пройденное расстояние, в этом уголdegree, поскольку это индикатор выполнения, общая сумма равна 100, а угол окружности здесь равен Math.PI*2,25 - Math.PI*0,75 = Math.PI * 1,75. Тогда угол 1% равен Math.PI*.1.5/100.
  3. Нам также нужно иметь переменную для записи процента текущего движенияtol

Подводя итог, положим классconstructorПреобразуйте внешний вид следующим образом:

constructor(obj = {}){
    /*
    * speed -- 速度
    * color -- 颜色
    * size -- 大小
    * lineWidth -- 线宽
    * location -- 圆心位置
    * text -- 文字
    * value -- 圆环滚动的值 ,这里指是百分比
    */
    this.speed = obj.speed || 0.1;
    this.color = obj.color || '#ffeedd';
    // 180 是UI的大体尺寸是在180px,以1920为基准
    this.size = Math.ceil((obj.size || 80) * (canvas.width / 180));
    this.lineWidth = obj.lineWidth || 10;
    this.location = obj.location || {x: canvas.width/2, y: canvas.height/2};
    this.textName = obj.text || '第二行文字title';
    this.value = obj.value || 0;
    // 这里是圆的终点减去圆的起点
    this.degree = Math.PI*1.5/100;
    this.animate = this.animate.bind(this);
    this.tol = 0;
}

drawCircleLay()Конечная точка рисования метода управляется с помощью переменной

drawCircleLay(){
    // 绘制进度条
    if (this.value == 0) return;
    ctx.beginPath();
    var gradient = ctx.createLinearGradient(0, this.linearLocation().start, 0, this.linearLocation().end);
    gradient.addColorStop(0, '#0f6cd9');
    gradient.addColorStop(1, '#05a6da');
    ctx.strokeStyle = gradient;
    ctx.lineWidth = this.lineWidth;
    ctx.lineCap = "round";
    //这里改成用变量来控制 
    ctx.arc(this.location.x, this.location.y, this.size, Math.PI * 0.75, Math.PI*0.75+this.tol * this.degree, false);
    ctx.stroke();
}

drawTextPercent()ПеременнаяtolКоличество прогресса, отображаемое в данный момент входящим эффектом

drawTextPercent(percent){
    // 绘制进度文字
    ctx.beginPath();
    ctx.font = `${this.size / 2.5}px Arial`;
    ctx.textAlign="center";
    ctx.fillStyle="#192f47";
    ctx.fillText(`${parseInt(percent)}%`, this.location.x, this.location.y);
    ctx.stroke();
}

Датьanimate()метод плюс RAF, чтобы заставить его двигаться

animate(){
    window.requestAnimationFrame(this.animate);
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    this.drawBg();
    this.drawCircleLay();
    this.drawCenterCircle();
    this.drawCenterBorderCircle();
    this.drawTextPercent(this.tol);
    this.drawTextName(this.textName);
    if (this.tol < this.value) { this.tol += this.speed }
}

На данный момент диск прогресса завершен. На самом деле, есть еще много возможностей для улучшения, таких как более разумная упаковка, мониторингwindow.resizeВремя для изменения размера и так далее. .

Адрес экземпляра:codepen