2

(我是 Javascript/HTML/CSS 编码的新手,所以请原谅我糟糕的风格!)

我想以静态速度沿该数组中的一组坐标为数组中的对象设置动画:

数组示例:

_myPtArr = [{x:297, y:30},{x:299,y:47},{x:350,y:56},{x:305,y:176},{x:278,y:169},
{x:303,y:108},{x:269,y:79},{x:182,y:90},{x:137,y:81},{x:173,y:33},{x:231,y:38}];

我所做的是;创建一个在程序开始时运行的函数以接收任何数组,并将距离属性(名为“dis”)添加到它计算距离的两个点中的第一个点,如下所示:

function findDist(array) {
    for (var i = 0; i<array.length-1;i++){
        var p = array[i],
            q = array[i+1],
            dx   = p.x - q.x,         
            dy   = p.y - q.y,         
            dist = Math.sqrt(dx*dx + dy*dy);
            array[i].dis=dist;
    }
}

我这样做是因为我为它们设置动画的方式是创建一个名为“_tick”的变量,它将通过从 0 到 1 递增将它们从一个点移动到另一个点,0 是起点,1 是终点。我打算将刻度乘以距离,以便不同的线条长度以相同的速度进行动画处理。但我还没有得到这样的工作!我被困住了!这是我刚才所说的功能:

function calculateInnerPts(pts, pos){
        var ptArr = [];
        console.log(pts.length);
        for(var i = 0; i < pts.length-1; i++){
           ptArr[i] = {x: pts[i].x + (pts[i+1].x - pts[i].x) * pos, y: pts[i].y + 
         (pts[i+1].y - pts[i].y) * pos};
        } 
        return ptArr;
 }  

在这样的系列中被调用:

在 enterFrameHandler (不断迭代)中,“section”只是递增以获取每组 2 个点/对象,所以在 _tick 达到“1”之后,section++。:

ballAnim([_myPtArr[_section],_myPtArr[_section+1]],_tick);

然后在外面:

function ballAnim(pts,pos){
    var iPts = pts;

    for(var i = 0; i < pts.length-1; i++){
        iPts = calculateInnerPts(iPts, pos);  
    }

    drawBall(iPts[0]);
 }

最后,我画了一个球:

function drawBall(pts){
    var w = 6,
        h = 2;
    ctx.clearRect(0,0,400,300);
    ctx.beginPath();
    ctx.arc(pts.x, pts.y, 5, 0, Math.PI * 2);
    ctx.fillStyle="blue";
    ctx.fill();
    ctx.strokeStyle="black";
    ctx.closePath();
    ctx.stroke();
}

鉴于这个系统,或者甚至是实现相同效果的另一种更好的方法,任何人都可以阐明我如何实现这一点吗?我想放入任何数组并获得相同的结果(意味着球以静态速度动画)!

注意:除了普通的 Javascript/HTML/CSS,我不想使用任何东西;没有API!

非常感谢大家,这个社区太棒了,这是我的第一篇文章,所以放轻松!XD

4

3 回答 3

5

我敢肯定上面的答案更优雅,但我整个早上都在研究这段代码,我想分享......

代码演示在这里

//requestAnimFrame shim via Paul Irish
window.requestAnimFrame = (function(callback){
    return (window.requestAnimationFrame || 
        window.webkitRequestAnimationFrame || 
        window.mozRequestAnimationFrame || 
        function(callback){window.setTimeout(callback, 1000/60);});
        })();

//main code
window.onload = function(){
    //setup canvas
    var canvas = document.getElementById('mycanvas');
    var ctx = canvas.getContext('2d');

    //variables
    var canvasWidth = 400, canvasHeight = 400;    //as set up in the <canvas> tag
    var animationTime = 10000;                    //animation length in milliseconds

    //clear canvas function
    var clearCanvas = function(){
        ctx.clearRect(0, 0, canvasWidth, canvasHeight);
        };

    //define the line; build function for displaying it
    var _myPtArr = [
        {x:297, y:30}, {x:299,y:47}, {x:350,y:56}, {x:305,y:176},
        {x:278,y:169}, {x:303,y:108}, {x:269,y:79}, {x:182,y:90},
        {x:137,y:81}, {x:173,y:33}, {x:231,y:38}
        ];
    var lineColor = 'red';
    var drawLine = function(){
        ctx.beginPath();
        ctx.moveTo(_myPtArr[0].x, _myPtArr[0].y);
        for(var i=1, z=_myPtArr.length; i<z; i++){
            ctx.lineTo(_myPtArr[i].x, _myPtArr[i].y);
            }
        ctx.strokeStyle = lineColor;
        ctx.stroke();
        };

    //define the ball; build function for displaying it
    var myBall = {x: 0, y: 0, radius: 5, fillStyle: 'blue', strokeStyle: 'black'};
    var drawBall = function(){
        ctx.beginPath();
        ctx.arc(myBall.x, myBall.y, myBall.radius, 0, (Math.PI*2));
        ctx.closePath();
        ctx.fillStyle = myBall.fillStyle;
        ctx.strokeStyle = myBall.strokeStyle;
        ctx.fill();
        ctx.stroke();
        };

    //build an array of objects for each line to help ball position calculations
    var myBallVectors = [];
    var fullDistance = 0, p, q, dx, dy, tempDistance, tempTime, fullTime = 0;
    for(var i=1, z=_myPtArr.length; i<z; i++){
        p = _myPtArr[i-1], q = _myPtArr[i];
        dx = q.x - p.x;
        dy = q.y - p.y;
        tempDistance = Math.sqrt((dx*dx)+(dy*dy));
        fullDistance += tempDistance;
        myBallVectors.push({x: p.x, y: p.y, dX: dx, dY: dy, magnitude: tempDistance});
        }
    for(var i=0, z=myBallVectors.length; i<z; i++){
        myBallVectors[i]['start'] = fullTime * animationTime;
        tempTime = myBallVectors[i].magnitude/fullDistance;
        fullTime += tempTime
        myBallVectors[i]['duration'] = tempTime * animationTime;
        myBallVectors[i]['finish'] = fullTime * animationTime;
        }

    //function to work out where ball is in space and time
    var myBallPosition = function(){
        if(Date.now() > breakTime){
            //move on to next line
            currentLine++;
            //check to see if animation needs to finish
            if(currentLine >= myBallVectors.length){
                doAnimation = false;
                }
            //otherwise, set new break point
            else{
                breakTime = startTime + parseInt(myBallVectors[currentLine].finish);
                }
            }
        if(doAnimation){
            //calculate ball's current position
            var timePassed = Date.now() - (startTime + myBallVectors[currentLine].start)
            var percentageLineDone = timePassed/myBallVectors[currentLine].duration;
            myBall.x = myBallVectors[currentLine].x + (myBallVectors[currentLine].dX * percentageLineDone);
            myBall.y = myBallVectors[currentLine].y + (myBallVectors[currentLine].dY * percentageLineDone);
            }
        };

    //setup and display initial scene
    drawLine();
    myBall.x = _myPtArr[0].x;
    myBall.y = _myPtArr[0].y;
    drawBall();

    //animation loop function
    animate = function(){
        clearCanvas();
        drawLine();
        myBallPosition();
        drawBall();
        if(doAnimation){
            requestAnimFrame(function(){
                animate();
                });
            }
        };

    //setup control variables, and start animation
    var startTime = Date.now(), currentLine = 0;
    var breakTime = startTime + parseInt(myBallVectors[currentLine].finish);
    var doAnimation = true;
    animate();

    };
于 2013-08-24T12:32:24.263 回答
4

以均匀的速度在多段线上为对象设置动画

什么是速度?

速度是每秒(或分钟、小时等)行进的距离。

例如:速度是英里每小时。

因此,以均匀的速度制作动画实际上意味着以每秒均匀的距离制作动画。

在多段线上以均匀的速度制作动画实际上意味着在多段线上以均匀的距离放置球,而不管每条线段的结束位置。

在此处输入图像描述

重要的方面是球必须始终保持相同的距离。

例如:如果您需要每秒行进 300 像素,则可能必须经过许多点,直到达到 300 像素的距离。或者,如果这些点相距甚远,您甚至可能无法完全到达下一个点。

以下代码基于您的 _myPtArr 和 findDist。

它沿着由 _myPtArr 定义的折线以均匀的距离投掷球。

总折线分为任意 20 个均匀长度的步长。

每个新球每 1/4 秒掉落一次。

您可以通过以下任一方式更改下降速度:

  • 更改完成折线所需的步数(球)。
  • 更改计时器间隔,使球落得更快/更慢。

为了使单个球看起来在移动,只需在绘制下一个球之前清除前一个球即可。

为了让球更顺畅地移动:

  • 增加步数,可能增加到 200。
  • 减少时间间隔,可能到 25。

这是代码和小提琴:http: //jsfiddle.net/m1erickson/3Fmm2/

<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>

<style>
    body{ background-color: ivory; }
    #canvas{border:1px solid red;}
</style>

<script>
$(function(){

    var canvas=document.getElementById("canvas");
    var ctx=canvas.getContext("2d");

    _myPtArr = [{x:297, y:30},{x:299,y:47},{x:350,y:56},{x:305,y:176},{x:278,y:169},
    {x:303,y:108},{x:269,y:79},{x:182,y:90},{x:137,y:81},{x:173,y:33},{x:231,y:38}];

    var totDistance=0;

    for (var i = 0; i<_myPtArr.length-1;i++){
        var p = _myPtArr[i],
            q = _myPtArr[i+1],
            dx   = p.x - q.x,         
            dy   = p.y - q.y,         
            dist = Math.sqrt(dx*dx + dy*dy);
            totDistance+=dist;
            _myPtArr[i].dist=dist;
            _myPtArr[i].untraveled=dist;
    }

    var steps=20;
    var distancePerStep=totDistance/steps;
    var totTraveled=0;
    var currentPoint=0;

    // drop first ball 
    drawBall(_myPtArr[0].x,_myPtArr[0].y);

    animate();

    function animate(){

        var mustTravel=distancePerStep;

        while(mustTravel>0){

            // check if we're done
            if(currentPoint >= _myPtArr.length-1){console.log("done"); return;}

            var pt0=_myPtArr[currentPoint];
            var pt1=_myPtArr[currentPoint+1];
            var dx=pt1.x-pt0.x;
            var dy=pt1.y-pt0.y;
            var lastX,lastY;

            if(pt0.untraveled<mustTravel){

                // travel this whole segment
                drawSegment(pt0,pt1);
                lastX=pt1.x;
                lastY=pt1.y;

                // and reduce d by length traveled
                mustTravel -= pt0.untraveled;
                pt0.untraveled = 0;
                if(mustTravel<1){mustTravel=0;}

                // go onto the next point
                currentPoint++;

            }else{

                // travel only part of this segment
                // It has enough available length to complete travel

                // start at the previously traveled point on the segment
                var prevTraveled = pt0.dist - pt0.untraveled;
                var x1 = pt0.x + dx * prevTraveled/pt0.dist;
                var y1 = pt0.y + dy * prevTraveled/pt0.dist;

                // travel only part of segment
                var x2 = x1 + dx * mustTravel/pt0.dist;
                var y2 = y1 + dy * mustTravel/pt0.dist;

                // draw a segment
                drawSegment({x:x1,y:y1},{x:x2,y:y2});
                lastX=x2;
                lastY=y2;

                // update segement and untraveled
                pt0.untraveled -= mustTravel;
                mustTravel=0;;

            }

        }

        // drop a ball 
        drawBall(lastX,lastY);

        setTimeout(animate,250);
    }

    function drawSegment(pt0,pt1){
    console.log("segment:"+pt0.x+"/"+pt0.y+" -- "+pt1.x+"/"+pt1.y);
        ctx.beginPath();
        ctx.moveTo(pt0.x,pt0.y);
        ctx.lineTo(pt1.x,pt1.y);
        ctx.stroke();
    }
    function drawBall(x,y){
    console.log("ball:"+x+"/"+y);
        ctx.beginPath();
        ctx.arc(x,y,5,0,Math.PI*2,false);
        ctx.closePath();
        ctx.fill();
    }


}); // end $(function(){});
</script>

</head>

<body>
    <canvas id="canvas" width=360 height=350></canvas>
</body>
</html>
于 2013-08-23T21:40:17.840 回答
1

我不确定你的代码是如何执行的,但我没有看到控制动画速度的函数或其他类型的代码。

您必须知道的第一件事是,人眼要感知任何平滑的变化,它大多数发生在不到 30 毫秒的时间内。您需要的第二件事是定期运行的函数,可以使用setTimeoutsetInterval来实现。

所以第一步是通过 setTimeout 或 setInterval 间接执行动画函数,我建议你以 30 毫秒的速率开始,然后根据代码的性能调整时间间隔。

一旦你有了它,你需要知道动画函数必须执行多少次才能达到所需的速度,例如,如果你想让动画持续 1 秒,你的动画函数必须执行 33 次,所以你的观点(s) 每次必须移动距离/33 像素。然后将以每个动画帧的像素为单位测量速度...您必须记住,由于距离是用直线测量的,因此速度也具有直线含义,因此您不能将其添加到 x 和 y,因为生成的速度将与您的预期不同,因此正确的方法是:

speed = distance/frames;
.
.
.
dx = end.x-current.x;
dy = end.y-current.y;
d = Math.sqrt(dx*dx+dy*dy);
current.x += speed*dx/d;
current.y += speed*dy/d;

更有效的方法是:

dx = end.x-start.x;
dy = end.y-start.y;
speed.x = dx/frames;
speed.y = dy/frames;
.
.
.
current.x += speed.x;
current.y += speed.y;

...因此您将有两个速度控制:每秒帧数和每帧像素数。通过这种方式,您可以以完全相同的速度为所有球设置动画,或者以成比例的速度为它们设置动画,以便所有球同时开始和结束移动,使它们以固定速度移动、加速、减速和各种效果......由你决定。

于 2013-08-23T15:42:31.817 回答