transition в canvas

127
25 января 2020, 09:00

Можно ли элементу в canvas добавить transition во время анимации?

<!DOCTYPE html> 
<html lang="en"> 
<head> 
    <meta charset="UTF-8"> 
    <title>Speedometer</title> 
    <link rel="stylesheet" href="../css/common.css"> 
    <style> 
        *{ 
            background: #fff; 
            font-family: 'SF Pro Display',sans-serif!important; 
            margin: 0; 
            padding: 0; 
        } 
        .container{ 
            display: flex; 
            justify-content: center; 
            align-items: center; 
            flex-direction: column; 
            position: relative; 
        } 
        .data_about_load{ 
            position: relative; 
            top: -60px; 
            opacity: 0; 
        } 
        .data_about_load:before{ 
            content: ""; 
            display: inline-block; 
            width: 20px; 
            height: 20px; 
            background: url(../img/svg/add/check_green.svg) center no-repeat; 
            background-size: contain; 
            position: relative; 
            top: 4px; 
            margin-right: 12px; 
        } 
        #canvas{ 
            opacity: 0; 
        } 
        .data{ 
            font-size: 16px; 
            font-weight: 600; 
            color: #0a0a0a; 
        } 
        .bold_time{ 
            font-weight: 700; 
        } 
 
        /*animations*/ 
        .fade-in-left { 
            animation: fade-in-left 0.6s cubic-bezier(0.390, 0.575, 0.565, 1.000) both; 
        } 
        .slide-in-fwd-center { 
            animation: slide-in-fwd-center 0.6s cubic-bezier(0.250, 0.460, 0.450, 0.940) both; 
        } 
        @keyframes fade-in-left { 
            0% { 
                transform: translateX(-50px); 
                opacity: 0; 
            } 
            100% { 
                transform: translateX(0); 
                opacity: 1; 
            } 
        } 
        @keyframes slide-in-fwd-center { 
            0% { 
                transform: translateZ(-1400px); 
                opacity: 0; 
            } 
            100% { 
                transform: translateZ(0); 
                opacity: 1; 
            } 
        } 
    </style> 
</head> 
<body> 
<div class="container"> 
    <canvas id="canvas" width="500" height="500"></canvas> 
    <div class="data_about_load"><span class="data">Время загрузки <span class="bold_time">0.1 сек</span></span></div> 
</div> 
<script> 
    const canvas = document.getElementById("canvas"), 
        ctx = canvas.getContext("2d"), 
 
        counterClockwise = false, 
        proportions={ 
            general: { 
                // general settings 
                middleX : canvas.width / 2, 
                middleY : canvas.height / 2, 
                radius : 240, 
            }, 
            ticks: { 
                //ticks settings 
                tickOffsetFromArc : canvas.width / 40, 
            }, 
            digits: { 
                digitsOffsetFromArc : canvas.width / 15, 
            }, 
            indicator: { 
                quarterY: (canvas.height / 2)/1.7 
            } 
        }, 
        colorPalette= { 
            // ticks palette 
            ticks: { 
                red: { 
                    redTickColor: "#FF0000" 
                }, 
                yellow: { 
                    darkYellowTickColor: "#F9AF00" 
                }, 
                green: { 
                    greenTickColor: "#13B74B", 
                    whiteFillColor: "#FFF", 
                    greenShadowColor: "#a8bbaa" 
                } 
            }, 
            //arrow color 
            arrow: { 
                bgBlue: "#3970eb", 
                shadowColor: "rgba(66,115,247,0.5)" 
            }, 
            //text color 
            text: "#0a0a0a", 
            diagram:{ 
                bg :{ 
                    gradient:[ 
                    "#e0f6e8", 
                    "#deede4" 
                    ], 
                    lines: "#ecedec" 
                } 
            } 
        }, 
        fonts={ 
            digitsFont: "bold 20px SF Pro Display", 
            indicator: "600 80px SF Pro Display", 
            diagramFont: "bold 10px SF Pro Display" 
        }, 
 
        // numbers setting 
        digits = [0, 20, 40, 50, 60, 70, 80, 90, 100], 
 
        //zones 
        zonesCount = digits.length - 1; 
 
    // Arrow settings 
    let arrowValueIndex = -2.45, 
 
    // beginning and ending of our arc. Sets by radius*pi 
        startAngleIndex = .75, 
        endAngleIndex = 2.25, 
        step = (endAngleIndex - startAngleIndex) / zonesCount, 
        allDataDiagram=[], 
        forVisibleElem=true; 
 
    /*draw zones*/ 
    let DrawZones = function () { 
        const greyZonesCount = zonesCount / 1.6; 
              greenZonesCount = zonesCount - greyZonesCount, 
              startAngle = (startAngleIndex - 0.02) * Math.PI, 
              endGreyAngle = (startAngleIndex + greyZonesCount * step) * Math.PI, 
              endGreenAngle = (endAngleIndex + 0.02) * Math.PI, 
 
              //zones' options 
              sectionOptions = [ 
                  { 
                      startAngle: startAngle, 
                      endAngle: endGreyAngle, 
                      color: "#e7e7e7", 
                      zoneLineWidth: 2 
                  }, 
                  { 
                      startAngle: endGreyAngle, 
                      endAngle: endGreenAngle, 
                      color: "#13b74b", 
                      zoneLineWidth: 5 
                  }, 
              ]; 
 
        //draw zones 
        this.DrawZone = function (options) { 
            ctx.beginPath(); 
            ctx.arc(proportions.general.middleX, proportions.general.middleY, proportions.general.radius, options.startAngle, options.endAngle, counterClockwise); 
            ctx.lineWidth = options.zoneLineWidth; 
            ctx.strokeStyle = options.color; 
            ctx.lineCap = "round"; 
            ctx.stroke(); 
        }; 
 
        sectionOptions.forEach(options => this.DrawZone(options)); 
    }; 
 
    /*draw dots*/ 
    let DrawTicks = function () { 
        startAngleIndex = .73, 
        endAngleIndex = 2.27, 
        step = (endAngleIndex - startAngleIndex) / zonesCount; 
        this.DrawTick = function (angle,count) { 
 
            let fromX = proportions.general.middleX + (proportions.general.radius - proportions.ticks.tickOffsetFromArc) * Math.cos(angle), 
                fromY = proportions.general.middleY + (proportions.general.radius - proportions.ticks.tickOffsetFromArc) * Math.sin(angle), 
                toX = proportions.general.middleX + (proportions.general.radius + proportions.ticks.tickOffsetFromArc) * Math.cos(angle), 
                toY = proportions.general.middleY + (proportions.general.radius + proportions.ticks.tickOffsetFromArc) * Math.sin(angle), 
 
                centerOfDotX=(fromX+toX)/2, 
                centerOfDotY=(fromY+toY)/2; 
            ctx.beginPath(); 
            ctx.arc(centerOfDotX,centerOfDotY,6,0,Math.PI*2,true); 
            if (count<6){ 
                switch (count) { 
                    case 1: 
                    case 2: 
                    case 3: 
                        ctx.fillStyle=colorPalette.ticks.red.redTickColor; 
                        break; 
                    default: 
                        ctx.fillStyle=colorPalette.ticks.yellow.darkYellowTickColor; 
                        break; 
                } 
            }else{ 
                ctx.fillStyle=colorPalette.ticks.green.whiteFillColor; 
                ctx.strokeStyle=colorPalette.ticks.green.greenTickColor; 
                ctx.shadowColor = colorPalette.ticks.green.greenShadowColor; 
                ctx.shadowBlur = 15; 
                ctx.shadowOffsetX = 0; 
                ctx.shadowOffsetY = 0; 
                ctx.stroke(); 
            } 
            ctx.fill(); 
            ctx.closePath(); 
            ctx.shadowBlur =0; 
        }; 
        let count=0; 
        for (let i = startAngleIndex; i <= endAngleIndex; i += step) { 
            let angle = i * Math.PI; 
            count++; 
            this.DrawTick(angle,count); 
        } 
    }; 
 
    //draw numbers 
    let DrawDigits = function () { 
        let angleIndex = startAngleIndex; 
 
        digits.forEach(function (digit) { 
            let angle = angleIndex * Math.PI, 
                    x = proportions.general.middleX + (proportions.general.radius - proportions.digits.digitsOffsetFromArc) * Math.cos(angle), 
                    y = proportions.general.middleY + (proportions.general.radius - proportions.digits.digitsOffsetFromArc) * Math.sin(angle); 
 
            angleIndex += step; 
 
            ctx.font = fonts.digitsFont; 
            ctx.fillStyle = colorPalette.text; 
            ctx.textAlign = "center"; 
            ctx.textBaseline = "middle"; 
            ctx.fillText(digit, x, y); 
        }); 
    }; 
 
    /*draw arrow*/ 
    let DrawArrow = function () { 
        ctx.beginPath(); 
        ctx.lineCap = "round"; 
        ctx.moveTo(proportions.general.middleX-15, proportions.general.middleY); 
        ctx.lineTo(proportions.general.middleX, proportions.general.middleY-150); 
        ctx.lineTo(proportions.general.middleX+15, proportions.general.middleY); 
        ctx.closePath(); 
        ctx.strokeStyle = colorPalette.arrow.bgBlue; 
        ctx.fillStyle=colorPalette.arrow.bgBlue; 
        ctx.shadowColor=colorPalette.arrow.shadowColor; 
        ctx.shadowBlur = 15; 
        ctx.shadowOffsetX = 0; 
        ctx.shadowOffsetY = 0; 
        ctx.stroke(); 
        ctx.fill(); 
        ctx.shadowBlur =0; 
    }; 
    let oldText=0; 
    /*draw indicator's number*/ 
    let DrawIndicator=function (val) { 
        ctx.font = fonts.indicator; 
        ctx.fillStyle = colorPalette.ticks.green.whiteFillColor; // or whatever color the background is. 
        ctx.fillText(oldText, proportions.general.middleX, proportions.indicator.quarterY); 
        ctx.fillStyle = colorPalette.text; 
        ctx.fillText(`${val}`, proportions.general.middleX, proportions.indicator.quarterY); 
        ctx.font = fonts.digitsFont; 
        ctx.fillText('points',proportions.general.middleX,proportions.indicator.quarterY+proportions.general.middleY/5); 
        oldText=val; 
    }; 
    let indicatorNum=0; 
 
    /*renderer*/ 
    function renderer() { 
        ctx.clearRect(0,0,canvas.width,canvas.height); 
 
        DrawZones(); 
        DrawTicks(); 
        DrawDigits(); 
        Diagram(); 
        DrawIndicator(indicatorNum); 
        ctx.translate(proportions.general.middleX,proportions.general.middleY); 
        ctx.rotate(arrowValueIndex); 
        ctx.translate(-proportions.general.middleX,-proportions.general.middleY); 
 
        DrawArrow(); 
 
        ctx.translate(proportions.general.middleX,proportions.general.middleY); 
        ctx.rotate(-arrowValueIndex); 
        ctx.translate(-proportions.general.middleX,-proportions.general.middleY); 
 
    } 
 
    function val(value) { 
        let sector = Math.PI*0.385; 
        if (value < 40) 
            arrowValueIndex = value/40*sector - sector*2; 
        else 
            arrowValueIndex = (value-40)/60*sector*3 - sector; 
        let add=(val)=>{ 
            indicatorNum=val; 
            allDataDiagram.unshift(val); 
            allDataDiagram=allDataDiagram.slice(0,186); 
        }; 
        add(value); 
        // document.querySelector('span').textContent = value; 
        renderer(); 
    } 
 
 
    //кроссбраузерный requestAnimationFrame 
    let requestAnimFrame = (function(){ 
        return  window.requestAnimationFrame   || 
            window.webkitRequestAnimationFrame || 
            window.mozRequestAnimationFrame    || 
            window.oRequestAnimationFrame      || 
            window.msRequestAnimationFrame     || 
            function(callback){ 
                window.setTimeout(callback, 1000 / 60); 
            }; 
    })(); 
    let mainNum=0; 
    let max=0.5, min=-0.6; 
 
    let Engine=()=>{ 
        if (forVisibleElem) canvas.classList.add('slide-in-fwd-center'); 
        if (mainNum<70) mainNum+=.5; 
        if (mainNum>=70 && mainNum<97)mainNum+=.4; 
        if (mainNum>=97){ 
            if(forVisibleElem) mainNum+=.3; 
            else mainNum+=.03; 
        } 
        if (mainNum>=100){ 
            mainNum=98.7; 
            if (forVisibleElem) { 
                document.querySelector(".data_about_load").classList.add("fade-in-left"); 
                forVisibleElem=false; 
            } 
        } 
 
        val(Math.floor(mainNum)); 
        requestAnimFrame(Engine); 
    }; 
 
 
let Diagram=()=>{ 
    this.drawLines=(x,y,width,height)=>{ 
        ctx.lineWidth=1.5; 
        ctx.strokeStyle=colorPalette.diagram.bg.lines; 
        ctx.moveTo(x,y); 
        ctx.lineTo(width,height); 
        ctx.stroke(); 
    }; 
    this.drawCircles = (x,y,r)=>{ 
        ctx.beginPath(); 
      ctx.arc(x,y,r,2*Math.PI,false); 
      ctx.fillStyle=colorPalette.ticks.green.greenTickColor; 
      ctx.fill(); 
        ctx.closePath(); 
    }; 
    this.writeNums = (value,x,y)=>{ 
        ctx.font = fonts.diagramFont; 
        ctx.fillStyle = colorPalette.text; 
        ctx.textAlign = "center"; 
        ctx.textBaseline = "middle"; 
        ctx.fillText(value, x, y); 
    }; 
    this.drawD=()=>{ 
        // console.log(allDataDiagram); 
        for (var i=0; i<=widthD; i++){ 
            //draw bg gradient 
            ctx.fillRect(proportions.general.middleX/1.6+i, proportions.general.middleY*1.53+heightD, 1, -(allDataDiagram[i]*0.25-2)); 
            ctx.fillStyle=colorPalette.ticks.green.whiteFillColor; 
            ctx.fillRect(proportions.general.middleX/1.6+i,proportions.general.middleY*1.53,1,4+28-allDataDiagram[i]*0.25); 
            ctx.fillStyle=colorPalette.ticks.green.greenTickColor; 
            ctx.fillRect(proportions.general.middleX/1.6+i,proportions.general.middleY*1.53+4+28-allDataDiagram[i]*0.25,1,2); 
            ctx.fillStyle=gradientD; 
        } 
    }; 
    const widthD=186, 
          heightD=32, 
          gradientD=ctx.createLinearGradient(0,0,widthD/2,0); 
    gradientD.addColorStop(0,colorPalette.diagram.bg.gradient[0]); 
    gradientD.addColorStop(1,colorPalette.diagram.bg.gradient[0]); 
 
 
    //draw grey lines 
    this.drawLines(proportions.general.middleX/1.6,proportions.general.middleY*1.53,proportions.general.middleX/1.6,proportions.general.middleY*1.53+heightD); 
    this.drawLines(proportions.general.middleX/1.6+widthD,proportions.general.middleY*1.53,proportions.general.middleX/1.6+widthD,proportions.general.middleY*1.53+heightD); 
 
    //write numbers 
    this.writeNums(94,proportions.general.middleX/1.6, proportions.general.middleY*1.48); 
    this.writeNums(97,proportions.general.middleX/1.6+widthD, proportions.general.middleY*1.48); 
 
    //draw diagram 
    this.drawD(); 
    //draw circles 
    this.drawCircles(proportions.general.middleX/1.6,(proportions.general.middleY*1.53)+(32-94*0.25),3); 
    this.drawCircles(proportions.general.middleX/1.6+widthD,(proportions.general.middleY*1.53)+(32-97*0.25),3); 
}; 
 
 
    window.onload=Engine; 
 
</script> 
</body> 
</html>

Answer 1

CSS transition никак не применить к элементам на канве.

Для получения плавной анимации в общем случае необходимо получать значения в зависимости от времени а не прибавлять каждый кадр фиксированные значения:

Я изменил в Вашем примере одну функцию:

let delay = 2; // задержка перед анимацией
let duration = 3; // кол во секунд анимации роста значения
let time = new Date().getTime(); // Время начала анимации
let Engine = () => {
    // кол-во секунд с начала анимации
    let t = (new Date().getTime() - time) / 1000;
    val(+getValueFromTime(t).toFixed(1));
    requestAnimFrame(Engine);
};
// получить значение спидометра в зависимости от текущего времени
function getValueFromTime(t) {
    if (t < delay) // если еще не прошло время задержки
       return 0 // вернем 0
    t -= delay; // иначе вычитаем время задержки
    if (t < 0.95*duration) { // если прошло меньше 95 % времени анимации
        // переводим значение времени в значение при помощи функции сглаживания 
        // EasingFunctions.easeOutCubic (значение на входе должно быть от 0 до 1)
        return 98 * EasingFunctions.easeOutCubic(t/duration);
     }
     // иначе это функция от синуса от этого значения
     return 98 + Math.sin(t * 55);
}

Функции сглаживания взяты отсюда, больше функций сглаживания тут

<div class="container"> 
    <canvas id="canvas" width="500" height="500"></canvas> 
    <div class="data_about_load"><span class="data">Время загрузки <span class="bold_time">0.1 сек</span></span></div> 
</div> 
<script> 
 
let EasingFunctions = { 
  // no easing, no acceleration 
  linear: function (t) { return t }, 
  // accelerating from zero velocity 
  easeInQuad: function (t) { return t*t }, 
  // decelerating to zero velocity 
  easeOutQuad: function (t) { return t*(2-t) }, 
  // acceleration until halfway, then deceleration 
  easeInOutQuad: function (t) { return t<.5 ? 2*t*t : -1+(4-2*t)*t }, 
  // accelerating from zero velocity  
  easeInCubic: function (t) { return t*t*t }, 
  // decelerating to zero velocity  
  easeOutCubic: function (t) { return (--t)*t*t+1 }, 
  // acceleration until halfway, then deceleration  
  easeInOutCubic: function (t) { return t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1 }, 
  // accelerating from zero velocity  
  easeInQuart: function (t) { return t*t*t*t }, 
  // decelerating to zero velocity  
  easeOutQuart: function (t) { return 1-(--t)*t*t*t }, 
  // acceleration until halfway, then deceleration 
  easeInOutQuart: function (t) { return t<.5 ? 8*t*t*t*t : 1-8*(--t)*t*t*t }, 
  // accelerating from zero velocity 
  easeInQuint: function (t) { return t*t*t*t*t }, 
  // decelerating to zero velocity 
  easeOutQuint: function (t) { return 1+(--t)*t*t*t*t }, 
  // acceleration until halfway, then deceleration  
  easeInOutQuint: function (t) { return t<.5 ? 16*t*t*t*t*t : 1+16*(--t)*t*t*t*t } 
}; 
</script> 
 
<script> 
    const canvas = document.getElementById("canvas"), 
        ctx = canvas.getContext("2d"), 
 
        counterClockwise = false, 
        proportions={ 
            general: { 
                // general settings 
                middleX : canvas.width / 2, 
                middleY : canvas.height / 2, 
                radius : 240, 
            }, 
            ticks: { 
                //ticks settings 
                tickOffsetFromArc : canvas.width / 40, 
            }, 
            digits: { 
                digitsOffsetFromArc : canvas.width / 15, 
            }, 
            indicator: { 
                quarterY: (canvas.height / 2)/1.7 
            } 
        }, 
        colorPalette= { 
            // ticks palette 
            ticks: { 
                red: { 
                    redTickColor: "#FF0000" 
                }, 
                yellow: { 
                    darkYellowTickColor: "#F9AF00" 
                }, 
                green: { 
                    greenTickColor: "#13B74B", 
                    whiteFillColor: "#FFF", 
                    greenShadowColor: "#a8bbaa" 
                } 
            }, 
            //arrow color 
            arrow: { 
                bgBlue: "#3970eb", 
                shadowColor: "rgba(66,115,247,0.5)" 
            }, 
            //text color 
            text: "#0a0a0a", 
            diagram:{ 
                bg :{ 
                    gradient:[ 
                    "#e0f6e8", 
                    "#deede4" 
                    ], 
                    lines: "#ecedec" 
                } 
            } 
        }, 
        fonts={ 
            digitsFont: "bold 20px SF Pro Display", 
            indicator: "600 80px SF Pro Display", 
            diagramFont: "bold 10px SF Pro Display" 
        }, 
 
        // numbers setting 
        digits = [0, 20, 40, 50, 60, 70, 80, 90, 100], 
 
        //zones 
        zonesCount = digits.length - 1; 
 
    // Arrow settings 
    let arrowValueIndex = -2.45, 
 
    // beginning and ending of our arc. Sets by radius*pi 
        startAngleIndex = .75, 
        endAngleIndex = 2.25, 
        step = (endAngleIndex - startAngleIndex) / zonesCount, 
        allDataDiagram=[], 
        forVisibleElem=true; 
 
    /*draw zones*/ 
    let DrawZones = function () { 
        const greyZonesCount = zonesCount / 1.6; 
              greenZonesCount = zonesCount - greyZonesCount, 
              startAngle = (startAngleIndex - 0.02) * Math.PI, 
              endGreyAngle = (startAngleIndex + greyZonesCount * step) * Math.PI, 
              endGreenAngle = (endAngleIndex + 0.02) * Math.PI, 
 
              //zones' options 
              sectionOptions = [ 
                  { 
                      startAngle: startAngle, 
                      endAngle: endGreyAngle, 
                      color: "#e7e7e7", 
                      zoneLineWidth: 2 
                  }, 
                  { 
                      startAngle: endGreyAngle, 
                      endAngle: endGreenAngle, 
                      color: "#13b74b", 
                      zoneLineWidth: 5 
                  }, 
              ]; 
 
        //draw zones 
        this.DrawZone = function (options) { 
            ctx.beginPath(); 
            ctx.arc(proportions.general.middleX, proportions.general.middleY, proportions.general.radius, options.startAngle, options.endAngle, counterClockwise); 
            ctx.lineWidth = options.zoneLineWidth; 
            ctx.strokeStyle = options.color; 
            ctx.lineCap = "round"; 
            ctx.stroke(); 
        }; 
 
        sectionOptions.forEach(options => this.DrawZone(options)); 
    }; 
 
    /*draw dots*/ 
    let DrawTicks = function () { 
        startAngleIndex = .73, 
        endAngleIndex = 2.27, 
        step = (endAngleIndex - startAngleIndex) / zonesCount; 
        this.DrawTick = function (angle,count) { 
 
            let fromX = proportions.general.middleX + (proportions.general.radius - proportions.ticks.tickOffsetFromArc) * Math.cos(angle), 
                fromY = proportions.general.middleY + (proportions.general.radius - proportions.ticks.tickOffsetFromArc) * Math.sin(angle), 
                toX = proportions.general.middleX + (proportions.general.radius + proportions.ticks.tickOffsetFromArc) * Math.cos(angle), 
                toY = proportions.general.middleY + (proportions.general.radius + proportions.ticks.tickOffsetFromArc) * Math.sin(angle), 
 
                centerOfDotX=(fromX+toX)/2, 
                centerOfDotY=(fromY+toY)/2; 
            ctx.beginPath(); 
            ctx.arc(centerOfDotX,centerOfDotY,6,0,Math.PI*2,true); 
            if (count<6){ 
                switch (count) { 
                    case 1: 
                    case 2: 
                    case 3: 
                        ctx.fillStyle=colorPalette.ticks.red.redTickColor; 
                        break; 
                    default: 
                        ctx.fillStyle=colorPalette.ticks.yellow.darkYellowTickColor; 
                        break; 
                } 
            }else{ 
                ctx.fillStyle=colorPalette.ticks.green.whiteFillColor; 
                ctx.strokeStyle=colorPalette.ticks.green.greenTickColor; 
                ctx.shadowColor = colorPalette.ticks.green.greenShadowColor; 
                ctx.shadowBlur = 15; 
                ctx.shadowOffsetX = 0; 
                ctx.shadowOffsetY = 0; 
                ctx.stroke(); 
            } 
            ctx.fill(); 
            ctx.closePath(); 
            ctx.shadowBlur =0; 
        }; 
        let count=0; 
        for (let i = startAngleIndex; i <= endAngleIndex; i += step) { 
            let angle = i * Math.PI; 
            count++; 
            this.DrawTick(angle,count); 
        } 
    }; 
 
    //draw numbers 
    let DrawDigits = function () { 
        let angleIndex = startAngleIndex; 
 
        digits.forEach(function (digit) { 
            let angle = angleIndex * Math.PI, 
                    x = proportions.general.middleX + (proportions.general.radius - proportions.digits.digitsOffsetFromArc) * Math.cos(angle), 
                    y = proportions.general.middleY + (proportions.general.radius - proportions.digits.digitsOffsetFromArc) * Math.sin(angle); 
 
            angleIndex += step; 
 
            ctx.font = fonts.digitsFont; 
            ctx.fillStyle = colorPalette.text; 
            ctx.textAlign = "center"; 
            ctx.textBaseline = "middle"; 
            ctx.fillText(digit, x, y); 
        }); 
    }; 
 
    /*draw arrow*/ 
    let DrawArrow = function () { 
        ctx.beginPath(); 
        ctx.lineCap = "round"; 
        ctx.moveTo(proportions.general.middleX-15, proportions.general.middleY); 
        ctx.lineTo(proportions.general.middleX, proportions.general.middleY-150); 
        ctx.lineTo(proportions.general.middleX+15, proportions.general.middleY); 
        ctx.closePath(); 
        ctx.strokeStyle = colorPalette.arrow.bgBlue; 
        ctx.fillStyle=colorPalette.arrow.bgBlue; 
        ctx.shadowColor=colorPalette.arrow.shadowColor; 
        ctx.shadowBlur = 15; 
        ctx.shadowOffsetX = 0; 
        ctx.shadowOffsetY = 0; 
        ctx.stroke(); 
        ctx.fill(); 
        ctx.shadowBlur =0; 
    }; 
    let oldText=0; 
    /*draw indicator's number*/ 
    let DrawIndicator=function (val) { 
        ctx.font = fonts.indicator; 
        ctx.fillStyle = colorPalette.ticks.green.whiteFillColor; // or whatever color the background is. 
        ctx.fillText(oldText, proportions.general.middleX, proportions.indicator.quarterY); 
        ctx.fillStyle = colorPalette.text; 
        ctx.fillText(`${val}`, proportions.general.middleX, proportions.indicator.quarterY); 
        ctx.font = fonts.digitsFont; 
        ctx.fillText('points',proportions.general.middleX,proportions.indicator.quarterY+proportions.general.middleY/5); 
        oldText=val; 
    }; 
    let indicatorNum=0; 
 
    /*renderer*/ 
    function renderer() { 
        ctx.clearRect(0,0,canvas.width,canvas.height); 
 
        DrawZones(); 
        DrawTicks(); 
        DrawDigits(); 
        Diagram(); 
        DrawIndicator(indicatorNum); 
        ctx.translate(proportions.general.middleX,proportions.general.middleY); 
        ctx.rotate(arrowValueIndex); 
        ctx.translate(-proportions.general.middleX,-proportions.general.middleY); 
 
        DrawArrow(); 
 
        ctx.translate(proportions.general.middleX,proportions.general.middleY); 
        ctx.rotate(-arrowValueIndex); 
        ctx.translate(-proportions.general.middleX,-proportions.general.middleY); 
 
    } 
 
    function val(value) { 
        let sector = Math.PI*0.385; 
        if (value < 40) 
            arrowValueIndex = value/40*sector - sector*2; 
        else 
            arrowValueIndex = (value-40)/60*sector*3 - sector; 
        let add=(val)=>{ 
            indicatorNum=val; 
            allDataDiagram.unshift(val); 
            allDataDiagram=allDataDiagram.slice(0,186); 
        }; 
        add(value); 
        // document.querySelector('span').textContent = value; 
        renderer(); 
    } 
 
 
    //кроссбраузерный requestAnimationFrame 
    let requestAnimFrame = (function(){ 
        return  window.requestAnimationFrame   || 
            window.webkitRequestAnimationFrame || 
            window.mozRequestAnimationFrame    || 
            window.oRequestAnimationFrame      || 
            window.msRequestAnimationFrame     || 
            function(callback){ 
                window.setTimeout(callback, 1000 / 60); 
            }; 
    })(); 
 
    
    if (forVisibleElem)  
      canvas.classList.add('slide-in-fwd-center'); 
    
  let delay = 2; // задержка перед анимацией 
  let duration = 3; // кол во секунд анимации роста значения 
  let time = new Date().getTime(); // Время начала анимации 
 
  let Engine = () => { 
      // кол-во секунд с начала анимации 
      let t = (new Date().getTime() - time) / 1000; 
      val(+getValueFromTime(t).toFixed(1)); 
      requestAnimFrame(Engine); 
  }; 
 
// получить значение спидометра в зависимости от текущего времени 
function getValueFromTime(t) { 
 
    if (t < delay) // если еще не прошло время задержки 
       return 0 // вернем 0 
 
    t -= delay; // иначе вычитаем время задержки 
 
    if (t < 0.95*duration) { // если прошло меньше 95 % времени анимации 
        // переводим значение времени в значение при помощи функции сглаживания  
        // EasingFunctions.easeOutCubic (значение на входе должно быть от 0 до 1) 
        return 98 * EasingFunctions.easeOutCubic(t/duration); 
     } 
 
     // иначе это функция от синуса от этого значения 
     return 98 + EasingFunctions.easeInOutQuint(1-(1+Math.sin(t*22))/2); 
} 
 
let Diagram=()=>{ 
    this.drawLines=(x,y,width,height)=>{ 
        ctx.lineWidth=1.5; 
        ctx.strokeStyle=colorPalette.diagram.bg.lines; 
        ctx.moveTo(x,y); 
        ctx.lineTo(width,height); 
        ctx.stroke(); 
    }; 
    this.drawCircles = (x,y,r)=>{ 
        ctx.beginPath(); 
      ctx.arc(x,y,r,2*Math.PI,false); 
      ctx.fillStyle=colorPalette.ticks.green.greenTickColor; 
      ctx.fill(); 
        ctx.closePath(); 
    }; 
    this.writeNums = (value,x,y)=>{ 
        ctx.font = fonts.diagramFont; 
        ctx.fillStyle = colorPalette.text; 
        ctx.textAlign = "center"; 
        ctx.textBaseline = "middle"; 
        ctx.fillText(value, x, y); 
    }; 
    this.drawD=()=>{ 
        // console.log(allDataDiagram); 
        for (var i=0; i<=widthD; i++){ 
            //draw bg gradient 
            ctx.fillRect(proportions.general.middleX/1.6+i, proportions.general.middleY*1.53+heightD, 1, -(allDataDiagram[i]*0.25-2)); 
            ctx.fillStyle=colorPalette.ticks.green.whiteFillColor; 
            ctx.fillRect(proportions.general.middleX/1.6+i,proportions.general.middleY*1.53,1,4+28-allDataDiagram[i]*0.25); 
            ctx.fillStyle=colorPalette.ticks.green.greenTickColor; 
            ctx.fillRect(proportions.general.middleX/1.6+i,proportions.general.middleY*1.53+4+28-allDataDiagram[i]*0.25,1,2); 
            ctx.fillStyle=gradientD; 
        } 
    }; 
    const widthD=186, 
          heightD=32, 
          gradientD=ctx.createLinearGradient(0,0,widthD/2,0); 
    gradientD.addColorStop(0,colorPalette.diagram.bg.gradient[0]); 
    gradientD.addColorStop(1,colorPalette.diagram.bg.gradient[0]); 
 
 
    //draw grey lines 
    this.drawLines(proportions.general.middleX/1.6,proportions.general.middleY*1.53,proportions.general.middleX/1.6,proportions.general.middleY*1.53+heightD); 
    this.drawLines(proportions.general.middleX/1.6+widthD,proportions.general.middleY*1.53,proportions.general.middleX/1.6+widthD,proportions.general.middleY*1.53+heightD); 
 
    //write numbers 
    this.writeNums(94,proportions.general.middleX/1.6, proportions.general.middleY*1.48); 
    this.writeNums(97,proportions.general.middleX/1.6+widthD, proportions.general.middleY*1.48); 
 
    //draw diagram 
    this.drawD(); 
    //draw circles 
    this.drawCircles(proportions.general.middleX/1.6,(proportions.general.middleY*1.53)+(32-94*0.25),3); 
    this.drawCircles(proportions.general.middleX/1.6+widthD,(proportions.general.middleY*1.53)+(32-97*0.25),3); 
}; 
 
 
    window.onload=Engine; 
     
     
 
 
</script>

READ ALSO
PDF слайдер для сайта

PDF слайдер для сайта

Подскажите пожалуйста есть ли какая-нибудь HTML/JS библиотека-слайдер для файлов в форматеpdf с кнопками переключения next/prev? В идеале хотелось...

124
Запустить онлайн игру на node.js и вебсокетах

Запустить онлайн игру на node.js и вебсокетах

Есть репозиторий (не мой) https://githubcom/dwcares/realchess В котором написан скрипт онлайн шахмат

116
Проверка if несколько раз

Проверка if несколько раз

Есть необходимость при изменении значения переменной, менять текст в заголовке

120
Обновление блока js

Обновление блока js

Подскажите пожалуйста, есть такой код который обновляет div с id="com_project"

116