Html5画布弹性动画

时间:2016-03-06 13:33:54

标签: html5 html5-canvas

我希望通过使圆和线在拖动时返回其原始位置来为现有动画添加弹性。

我尝试过使用mouseclick和mousedown,但没有工作。这里是jsfiddle

https://jsfiddle.net/vinito/byw7to95/

<!DOCTYPE html>
<html>
<head>

<style type="text/css">
    #canvasOne
    {
        border: 1px solid black;
    }
</style>
<script src="jquery-1.7.2.min.js" type="text/javascript"></script>
</head>
<body>
    <div align="center">
        <canvas id="canvasOne" width="950" height="700">
        </canvas>
    </div>

    <script type="text/javascript">

    var myCanvas = document.getElementById("canvasOne");
    var myContext = myCanvas.getContext("2d");

    init();

    var numShapes;
    var shapes;
    var dragIndex;
    var dragging;
    var mouseX;
    var mouseY;
    var dragHoldX;
    var dragHoldY;
    var timer;
    var targetX;
    var targetY;
    var easeAmount;
    var bgColor;

    function init()
    {
        numShapes = 5;              
        shapes = [];

        makeShapes();
        drawScreen();   
        myCanvas.addEventListener("mousedown", mouseDownListener, false);   
    }

    function makeShapes()
    {
        var tempX;
        var tempY;
        var tempRad;        
        var tempGrad;
        var gradFactor = 2;

        for(var i = 0; i < numShapes; i++)
        {           
            //random position
            tempRad = 40;
            var centerX = myCanvas.width/2;
            var centerY = myCanvas.height/2;

            if(i == 0)
            {
                tempX = centerX
                tempY = centerY;
            }
            else
            {
                //tempX = Math.random() * (myCanvas.width - tempRad);
                //tempY = Math.random() * (myCanvas.height - tempRad);
                //150 can be actual radius in degrees
                tempX = centerX + 250 * Math.cos(2 * Math.PI * i / numShapes);
                tempY = centerY + 250 * Math.sin(2 * Math.PI * i / numShapes);
            }

            tempColor = "#4285F4";
            tempShape = {x: tempX, y: tempY, rad: tempRad, color: tempColor};

            shapes.push(tempShape);         
        }       
    }

    function mouseDownListener(evt)
    {
        var highestIndex = -1;

        var bRect = myCanvas.getBoundingClientRect();
        mouseX = (evt.clientX - bRect.left) * (myCanvas.width/bRect.width);
        mouseY = (evt.clientY - bRect.top) * (myCanvas.height/bRect.height);

        for(var i = 0; i < numShapes; i++)
        {
            if(hitTest(shapes[i], mouseX, mouseY))
            {
                dragging = true;
                if(i > highestIndex)
                {
                    dragHoldX = mouseX - shapes[i].x;
                    dragHoldY = mouseY - shapes[i].y;
                    highestIndex = i;
                    dragIndex = i;
                }               
            }
        }

        if(dragging)
        {
            window.addEventListener("mousemove", mouseMoveListener, false);
        }

        myCanvas.removeEventListener("mousedown", mouseDownListener, false);
        window.addEventListener("mouseup", mouseUpListener, false);

        if(evt.preventDefault)
        {
            evt.preventDefault;
        }

        return false;
    }

    function mouseUpListener(evt)
    {
        myCanvas.addEventListener("mousedown", mouseDownListener, false);
        window.removeEventListener("mouseup", mouseUpListener, false);

        if(dragging)
        {
            dragging = false;
            window.removeEventListener("mousemove", mouseMoveListener, false);
        }
    }

    function mouseMoveListener(evt)
    {
        var shapeRad = shapes[dragIndex].rad;

        var minX = shapeRad;
        var maxX = myCanvas.width - shapeRad;

        var minY = shapeRad;
        var maxY = myCanvas.height - shapeRad;

        //get mouse position correctly
        var bRect = myCanvas.getBoundingClientRect();
        mouseX = (evt.clientX - bRect.left)*(myCanvas.width / bRect.width);
        mouseY = (evt.clientY - bRect.top)*(myCanvas.height / bRect.height);

        //clamp x and y position to prevent object from dragging outside canvas
        posX = mouseX - dragHoldX;
        posX = (posX < minX) ? minX : ((posX > maxX) ? maxX : posX);
        posY = mouseY - dragHoldY;      
        posY = (posY < minY) ? minY : ((posY > maxY) ? maxY : posY);

        shapes[dragIndex].x = posX;
        shapes[dragIndex].y = posY;

        drawScreen();       
    }

    function hitTest(shape, mx, my)
    {
        var dx = mx - shape.x;
        var dy = my - shape.y;

        return(dx * dx + dy * dy < shape.rad * shape.rad);
    }

    //drawing both shape (line and circle) and screen

    function drawScreen()
    {
        myContext.fillStyle = "#ffffff";
        myContext.fillRect(0, 0, myCanvas.width, myCanvas.height);
        drawShapes();
    }

    function drawShapes()
    {

        //line
        for(var i = 1; i < numShapes; i++)
        {
            myContext.beginPath();
            myContext.moveTo(shapes[0].x, shapes[0].y);
            myContext.lineTo(shapes[i].x, shapes[i].y);
            myContext.stroke();
        }

        //circle        
        for(var i = 0; i < numShapes; i++)
        {
            myContext.fillStyle = shapes[i].color;
            myContext.beginPath();
            myContext.arc(shapes[i].x, shapes[i].y, shapes[i].rad, 0, 2*Math.PI, false);
            myContext.closePath();
            myContext.fill();
        }       
    }

</script>
</body>
</html>

1 个答案:

答案 0 :(得分:2)

这是将圆圈从原点移开并将其设置为动画的一种方法

移动圆圈(拖动或以编程方式)远离其原始位置

收听鼠标事件:

  • 在mousedown上,测试鼠标是否在圆圈内被击落。如果是,请保存鼠标位置并设置一个标志,指示拖动操作已经开始。

  • 在鼠标移动时,计算自上次mousemove事件以来鼠标移动的距离,并将圆移动该距离。

  • 在mouseup(或mouseout)上,清除isDragging标志,启动动画循环以将圆圈设置为原始位置,并设置一个标志,表示动画已经开始。

将圆圈设置回原始位置

使用requestAnimationFrame创建动画循环。

  • 计算动画运行的时间(==已用时间)。

  • 圆圈将从其dragDrop位置动画回原始位置。根据经过的时间计算当前的圆圈位置。您可以使用Robert Penner's Easing函数之一为动画添加缓动效果。

  • 清除画布并在当前回弹位置重绘圆圈

  • 如果动画完成(==圆圈返回其原点),则清除isAnimating标志。否则,如果动画尚未完成,请求另一个动画帧。

以下是带注释的示例代码和演示:

// canvas vars
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
function reOffset(){
    var BB=canvas.getBoundingClientRect();
    offsetX=BB.left;
    offsetY=BB.top;        
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }
window.onresize=function(e){ reOffset(); }

// drag vars
var isDown=false;
var startX,startY;

// animation vars
var originalX=50;
var originalY=50;
var radius=30;
var stretchedX=originalX;
var stretchedY=originalY;
var duration=1000;
var startTime=0;
var isAnimating=false;

// set canvas styles
ctx.fillStyle='skyblue';
ctx.strokeStyle='lightgray';
ctx.lineWidth=3;

// listen for mouse events
$("#canvas").mousedown(function(e){handleMouseDown(e);});
$("#canvas").mousemove(function(e){handleMouseMove(e);});
$("#canvas").mouseup(function(e){handleMouseUpOut(e);});
$("#canvas").mouseout(function(e){handleMouseUpOut(e);});

draw(originalX,originalY);

function animate(time){
    // not animating, just return
    if(!isAnimating){return;}
    
    // calc how long the animation has been running
    var elapsed=time-startTime;
    if(elapsed>duration){ elapsed=duration; }
    
    // calc the current x,y using easing
    var x=easeOutBounce(elapsed,stretchedX,originalX-stretchedX,duration);
    var y=easeOutBounce(elapsed,stretchedY,originalY-stretchedY,duration);

    // draw the scene
    ctx.clearRect(0,0,cw,ch);
    draw(x,y);

    // if the animation isn't done, request a new frame
    if(isAnimating && elapsed>=duration){
        // done, turn off animation
        isAnimating=false;            
    }else{
        // more animating to be done
        requestAnimationFrame(animate);
    }

}

function draw(x,y){
    // draw small circle in original position
    // draw larger circle in dragged position
    // connect 2 circles with line
    ctx.beginPath();
    ctx.moveTo(originalX,originalY);
    ctx.lineTo(x,y);
    ctx.moveTo(originalX,originalY);
    ctx.arc(originalX,originalY,5,0,Math.PI*2);
    ctx.moveTo(x,y);
    ctx.arc(x,y,radius,0,Math.PI*2);
    ctx.closePath();
    ctx.stroke();
    ctx.fill();
}


function handleMouseDown(e){
  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();
  
  startX=parseInt(e.clientX-offsetX);
  startY=parseInt(e.clientY-offsetY);

  // if the mouse is down in the circle
  // start dragging
  var dx=startX-originalX;
  var dy=startY-originalY;
  if(dx*dx+dy*dy<radius*radius){
      isAnimating=false;
      isDown=true; 
  }
}

function handleMouseUpOut(e){
  if(!isDown){return;}         

  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();
  
  mouseX=parseInt(e.clientX-offsetX);
  mouseY=parseInt(e.clientY-offsetY);

  // stop dragging, start animating
  isDown=false;
  stretchedX=mouseX;
  stretchedY=mouseY;
  isAnimating=true;
  startTime=performance.now();
  requestAnimationFrame(animate);
}

function handleMouseMove(e){
  if(!isDown){return;}

  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();

  mouseX=parseInt(e.clientX-offsetX);
  mouseY=parseInt(e.clientY-offsetY);

  // calc distance mouse has moved since last mousemove event
  var dx=mouseX-startX;
  var dy=mouseY-startY;
  startX=mouseX;
  startY=mouseY;

  // draw the scene
  ctx.clearRect(0,0,cw,ch);
  draw(mouseX,mouseY);
  
}

// easing functions 
// Thank you Robert Penner: http://robertpenner.com/easing/
function easeInBounce(t, b, c, d){
  return c - easeOutBounce(d-t, 0, c, d) + b;
}
function easeOutBounce(t, b, c, d){
  if ((t/=d) < (1/2.75)) {
    return c*(7.5625*t*t) + b;
  } else if (t < (2/2.75)) {
    return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
  } else if (t < (2.5/2.75)) {
    return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
  } else {
    return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
  }
}
function easeInOutBounce(t, b, c, d){
  if (t < d/2) return easeInBounce(t*2, 0, c, d) * .5 + b;
  return easeOutBounce(t*2-d, 0, c, d) * .5 + c*.5 + b;
}
body{ background-color: ivory; }
#canvas{border:1px solid red; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Drag circle. Circle returns to origin with easing.</h4>
<canvas id="canvas" width=500 height=300></canvas>