【问题标题】:Html5 canvas elasticity animationhtml5画布弹性动画
【发布时间】:2016-03-07 04:21:51
【问题描述】:

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

我尝试过使用 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>

【问题讨论】:

  • 事件和回调被正确触发,你需要思考你想要做什么的逻辑:在鼠标点击和拖动时,你希望你的形状跟随鼠标。在鼠标上,您想将形状恢复到以前的位置吗?如果是这样,您需要存储形状的先前位置。

标签: html html5-canvas


【解决方案1】:

这是一种将圆从原点移开并将其动画返回的方法

将一个圆圈(拖动或以编程方式)从其原始位置移开

监听鼠标事件:

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

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

  • 在 mouseup(或 mouseout)时,清除 isDragging 标志,启动动画循环以将圆圈动画返回到其原始位置,并设置一个指示动画已开始的标志。

将圆动画化回其原始位置

使用requestAnimationFrame 创建动画循环。

  • 计算动画已经运行了多长时间(==经过的时间)。

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

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

  • 如果动画完成(==circle 回到其原点),则清除 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>

【讨论】:

  • 我正在使用画布,但我的 2 个矩形有问题相互连接。这是我创建的 1 个 js bin:output.jsbin.com/zacacufuxa
  • @Learning 我不明白你的评论。您应该发布一个包含更多详细信息的新问题,以便我们更好地了解您想要做什么。
  • 看到我已经发布了这个问题:stackoverflow.com/questions/37208156/…
猜你喜欢
  • 1970-01-01
  • 2014-02-15
  • 2012-05-15
  • 2016-04-29
  • 2011-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-04
相关资源
最近更新 更多