我正在构建一个带有无限动画循环的相框。这是一个彩色矩形,每个边框都会打开。它是使用纯Javascript构建的,框架在画布上绘制。
如何永久重新创建效果?我希望这个框架能够永远地继续下去。
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/
答案 0 :(得分:1)
查看代码,逻辑中存在问题。循环时必须重置所有动画。而不是指向需要修改的代码,我将为画布提供一种更灵活的动画制作方法。
使用事件将动画链接在一起
动画并不像刚刚开始结束那么简单,你需要根据屏幕大小,用户交互等来链接动画。为此,您可以创建检查特定条件的事件,并调用函数(如果为true)。
灵活的动画
此外,您从未真正了解动画的外观和感觉。很多时候这个概念与现实不符,你需要修改动画。如果您在代码中设置了太多逻辑,这可能会非常耗时。
为了更轻松地修改动画,您可以分离出所有各个部分。有渲染(特定动画项目的样子),然后是关键帧和定义关键帧的属性。定时功能,用于控制动画的当前状态并检查事件。
动画项目
动画项表示从头到尾的一个动画。它有一个开始和结束关键帧,具有一组动画属性。对所有动画通用的更新函数采用两个关键帧并计算当前帧。然后它调用事件列表中的项目并触发任何需要的项目。有一个绘制函数,它使用当前状态绘制所需的内容,以及一个启动函数,它通过设置开始时间和重置所有事件来启动动画。
这是一个动画项目
的示例 {
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);
&#13;