javascript - 使用 Canvas 和 javascript 循环使用 if 语句的框架

标签 javascript function loops canvas requestanimationframe

我正在构建一个带有无限动画循环的相框。它是一个彩色矩形,每个边框都在不断延伸。它是使用纯 Javascript 构建的,框架是在 Canvas 上绘制的。

我怎样才能永久地重新创建效果?我希望这个框架永远持续下去。

var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');


 var objectSpeed = 8;

 var recTop = {
   x: -300,
    y: 0,
    width: 300,
    height: 20,
    isMoving: true

  };

 var recRight = {
     x: 480,
     y: -480,
    width: 20,
      height: 300,
    isMoving:false

 };

var recBottom = {
    x: 500,
   y: 480,
   width: 300,
    height: 20,
    isMoving:false

 };

 var recLeft = {
    x: 0,
    y: 500,
    width: 20,
    height: 300,
     isMoving:false

   };

 function drawRecTop(recTop, context) {
  context.beginPath();
   context.rect(recTop.x, recTop.y, recTop.width, recTop.height);
   context.fillStyle = '#FB0202';
   context.fill();

}


 function drawRecRight(recRight, context) {
     context.beginPath();
     context.rect(recRight.x , recRight.y , recRight.width, recRight.height);
      context.fillStyle = '#FB0202';
      context.fill();

    }

 function drawRecBottom(recBottom, context) {
     context.beginPath();
     context.rect(recBottom.x , recBottom.y , recBottom.width, recBottom.height);
     context.fillStyle = '#FB0202';
      context.fill();

}

  function drawRecLeft(recLeft, context) {
    context.beginPath();
    context.rect(recLeft.x , recLeft.y , recLeft.width, recLeft.height);
    context.fillStyle = '#FB0202';
     context.fill();

}


  var squareXSpeed = objectSpeed;
   var squareYSpeed = objectSpeed;


function animate(myRectangle, canvas, context, startTime) {


    if(recTop.x > canvas.width - recTop.width && !recRight.isMoving){
        //START RIGHT RECTANGLE MOVEMENT
       recRight.isMoving = true;
       recRight.y = - recRight.height + recRight.width;

    }



      if(recRight.y > canvas.height - recRight.height && !recBottom.isMoving){
        //START BOTTOM RECTANGLE MOVEMENT
       recBottom.isMoving = true;
       recBottom.x = canvas.width-recBottom.height;

     }



    if(recBottom.x < 0 && !recLeft.isMoving){
        //START LEFT RECTANGLE MOVEMENT
        // recLeft.y = - recLeft.width + recLeft.height;
          recLeft.isMoving = true;
         recLeft.y = canvas.height-recLeft.width;

      }


    if(recLeft.y < 0 && !recTop.isMoving){
        //START BOTTOM RECTANGLE MOVEMENT
       recTop.isMoving = true;
        recTop.x = -(canvas.width - recTop.width);
    }

    if(recTop.x > canvas.width && recLeft.isMoving){
       //TOP RECTANGLE HAS LEFT THE STAGE
       recTop.isMoving = false;

     }



    if(recTop.isMoving)recTop.x += objectSpeed;
     if(recRight.isMoving)recRight.y += objectSpeed;
     if(recBottom.isMoving)recBottom.x -= objectSpeed;
     if(recLeft.isMoving)recLeft.y -= objectSpeed;

// clear
context.clearRect(0, 0, canvas.width, canvas.height);

   drawRecTop(recTop, context);
   drawRecRight(recRight, context);
   drawRecBottom(recBottom, context);
    drawRecLeft(recLeft, context);


// request new frame
   requestAnimFrame(function() {
        animate(recLeft, canvas, context, startTime);
   });

}

这是我目前得到的: https://jsfiddle.net/jwp9ya5w/

最佳答案

Canvas 上的链式动画。

查看你的代码,你的逻辑有问题。循环时必须重置所有动画。我不会指出需要修改的代码,而是提供一种更灵活的 Canvas 动画方法。

使用事件将动画链接在一起

动画并不只是从头到尾播放那么简单,您需要根据屏幕尺寸、用户交互等来链接动画。为此,您需要创建检查特定条件并在为真时调用函数的事件。

灵活的动画

此外,您永远不会在一开始就真正知道动画的外观和感觉。很多时候概念与现实不符,需要修改动画。如果您在代码中设置了太多逻辑,这可能会很耗时。

为了更轻松地修改动画,您可以将所有不同的部分分开。先是渲染(特定动画项目的外观),然后是关键帧和定义关键帧的属性。控制动画当前状态和检查事件的计时函数。

动画项目

一个动画项代表一个从头到尾的动画。它有一个开始和结束关键帧,具有一组动画属性。所有动画通用的更新函数获取两个关键帧并计算当前帧。然后它调用事件列表中的项目并触发任何需要的项目。有一个 draw 函数使用当前状态绘制需要的东西,还有一个 start 函数通过设置开始时间和重置所有事件来启动动画。

这是一个动画项目的例子

    {
        animate : ["x","y","w","h"],// what to animate
        draw : drawFunc,            // function to draw
        update : animateFunc,       // function to set current anim state
        start : startFunc,          // function to start the animation
        startTime : null,           // this item's start time
        length : timePerSide,       // how long in seconds this animation is
        events : [{                 // list of events
                fired : false,      // true if fired ( to stop it from repeating)
                callback : null,    // callback to call when event fires
                // this function determines when to fire the event
                check : function(owner,time){
                    if(owner.current.x >= canvas.width - owner.current.w){
                        this.fired = true;
                        this.callback(time);
                    }
                },
            }
        ],
        current : {}, // defined when running holds the current state
        startKey : {  // the key frame defining the start position
            x : -boxLength,
            y : 0,
            w : boxLength,
            h : boxHeight,
        },
        endKey : {   // keyframe for end
            x : canvas.width,
            y : 0,
            w : boxLength,
            h : boxHeight,
        }
    },

通过更改关键帧设置可以轻松更改动画。如果你需要改变渲染你只需要改变绘图函数,如果你需要添加一个额外的动画值你只需将它添加到开始和结束关键帧,将它列在 animate 数组中并且更改绘图函数以包含新属性。

事件是一个数组,因此可以有多个,并且可以依赖于任何类型的条件。对于你的动画,我有 4 个动画项目,它们沿着边缘移动一个盒子。每个项目都有一个事件,用于检查当前框是否到达另一侧并调用下一个项目的 start 函数。这会在当前动画结束之前开始下一个动画。

它的外观可能比您的代码复杂一点,但使用这样的系统可以非常轻松地更改动画。

该片段显示了用于创建动画的此方法。它有很多评论,可以根据您的特定需求进行调整。

// create and add canvas
var createImage=function(w,h){var i=document.createElement("canvas");i.width=w;i.height=h;i.ctx=i.getContext("2d");return i;}
var canvas = createImage(200,200);
var ctx = canvas.ctx;
document.body.appendChild(canvas);




//==============================================================================
// common properties

const boxAnimProps = ["x","y","w","h"]; // List of keyframe properties to animate
const boxHeight = 10; // width of box if looking at it on the sides
const boxLength = 100; // length of box
const timePerSide = 2;  // time to animate each item


//==============================================================================
// common functions
// this function updates the time and check for events
const animateFunc = function(time){
    var i,prop;
    if(!this.started){
        return;
    }
    // set local (this item's) time 
    lTime = time - this.startTime;  // adjust to start time
    // set time clamped to start and end
    lTime = this.current.time = lTime < 0 ? 0 : lTime > this.length ? this.length: lTime;
    // normalise the time 0-1
    var nTime = lTime / this.length;
    for(i = 0; i < this.animate.length; i ++){
        prop = this.animate[i];
        this.current[prop] = (this.endKey[prop]-this.startKey[prop]) * nTime + this.startKey[prop];
    }
    // check for events
    for(i = 0; i < this.events.length; i ++){
        if(!this.events[i].fired){
            this.events[i].check(this,time);
        }
    }
};
// function starts an animtion item and resets any events it has
const startFunc = function(time){
    this.started = true;
    this.startTime = time;
    // reset events
    for(var i = 0;i < this.events.length; i ++){ 
        this.events[i].fired = false;
    }
}
// this function draws the item
const drawFunc = function(ctx){
    if(!this.started){
        return;
    }
    ctx.fillRect(this.current.x,this.current.y,this.current.w,this.current.h);
    
}



//==============================================================================
// the animation. Time is in seconds
// animation.start starts the animation
// animation.update(ctx,time) to update

var animation = {
    start : function(time){
        animation.items[0].start(time);
    },
    update : function(ctx,time){
        var i;
        var len = this.items.length;
        // update animation details
        for(i = 0; i < len; i ++){
            this.items[i].setTime(time);
        }
        // draw animation
        for(i = 0; i < len; i ++){
            this.items[i].draw(ctx);
        }        
    },
    items : [
        {
            animate : boxAnimProps,     // what to animate
            draw : drawFunc,            // function to draw
            setTime : animateFunc,      // function to update state
            start : startFunc,          // function to start the animation
            startTime : null,           // this items start time
            length : timePerSide,       // how long in seconds this animation is
            events : [{                 // list of events
                    fired : false,      // true if fired ( to stop it from repeating)
                    callback : null,    // callback to call when event fires
                    // this function determines when to fire the event
                    check : function(owner,time){
                        if(owner.current.x >= canvas.width - owner.current.w){
                            this.fired = true;
                            this.callback(time);
                        }
                    },
                }
            ],
            current : {}, // defined when running holds the current state
            startKey : {  // the key frame defining the start position
                x : -boxLength,
                y : 0,
                w : boxLength,
                h : boxHeight,
            },
            endKey : {   // keyframe for end
                x : canvas.width,
                y : 0,
                w : boxLength,
                h : boxHeight,
            }
        },{
            animate : boxAnimProps,
            draw : drawFunc, // function to draw
            setTime : animateFunc, // function to set current anim state
            start : startFunc, // function to start the animation
            startTime : null,
            length : timePerSide,
            events : [{
                    fired : false,
                    callback : null,
                    check : function(owner,time){
                        if(owner.current.y >= canvas.height - owner.current.h){
                            this.fired = true;
                            this.callback(time);
                        }
                    },
                }
            ],
            current : {}, // defined when running
            startKey : {
                x : canvas.width - boxHeight,
                y : -boxLength,
                w : boxHeight,
                h : boxLength,
            },
            endKey : {
                x : canvas.width - boxHeight,
                y : canvas.height,
                w : boxHeight,
                h : boxLength,
            }
        },{
            animate : boxAnimProps,
            draw : drawFunc, // function to draw
            setTime : animateFunc, // function to set current anim state
            start : startFunc, // function to start the animation
            startTime : null,
            length : timePerSide,
            events : [{
                    fired : false,
                    callback : null,
                    check : function(owner,time){
                        if(owner.current.x <= 0){
                            this.fired = true;
                            this.callback(time);
                        }
                    },
                }
            ],
            current : {}, // defined when running
            startKey : {
                x : canvas.width,
                y : canvas.height - boxHeight,
                w : boxLength,
                h : boxHeight,
            },
            endKey : {
                x : - boxLength,
                y : canvas.height - boxHeight,
                w : boxLength,
                h : boxHeight,
            }
        },{
            animate : boxAnimProps,
            draw : drawFunc, // function to draw
            setTime : animateFunc, // function to set current anim state
            start : startFunc, // function to start the animation
            startTime : null,
            length : timePerSide,
            events : [{
                    fired : false,
                    callback : null,
                    check : function(owner,time){
                        if(owner.current.y <= 0){
                            this.fired = true;
                            this.callback(time);
                        }
                    },
                }
            ],
            current : {}, // defined when running
            startKey : {
                x : 0,
                y : canvas.height,
                w : boxHeight,
                h : boxLength,
            },
            endKey : {
                x : 0,
                y : - boxLength,
                w : boxHeight,
                h : boxLength,
            }
        }
    ]
};
// set events. Item one calls item two and so on
animation.items[0].events[0].callback = animation.items[1].start.bind(animation.items[1]);
animation.items[1].events[0].callback = animation.items[2].start.bind(animation.items[2]);
animation.items[2].events[0].callback = animation.items[3].start.bind(animation.items[3]);
animation.items[3].events[0].callback = animation.items[0].start.bind(animation.items[0]);



// update the canvas
var firstFrame = true;
function update(time){
    if(firstFrame){
        firstFrame = false;
        animation.start(time / 1000); // set start time in seconds.
    }
    ctx.fillStyle = "#AAA";
    ctx.fillRect(0,0,canvas.width,canvas.height);
    ctx.fillStyle = "#F00";
    animation.update(ctx,time / 1000); // animate and convert time from ms to seconds
    requestAnimationFrame(update);
}
requestAnimationFrame(update);

关于javascript - 使用 Canvas 和 javascript 循环使用 if 语句的框架,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39888598/

相关文章:

java - 性能 (JAVA) ~ 循环中的字符串连接,带有前置和附加

python - 循环 Pandas 对象列表表现出奇怪的行为

javascript - AWS s3 对象的策略文档 - 什么 ACL

javascript - 替换 Javascript 中的插入符号

javascript - Jquery 事件不适用于动态追加的元素

php - 为什么 heder ('Location:index' )不工作但 window.location.href ='index.php' 工作?

vb.net - 这种从 vb.net 中的函数返回多个值的技术的名称是什么?

C:如何使数字始终向上舍入

r - 通过数据帧在 R 中自动执行多项计算

查询中的 MySQL 循环变量