我正在网站上工作,当你向下滚动一条线时会动画成一个正方形/矩形,效果很好,但有一点让我烦恼可能会有点困难一个要解决但在这里是:
当动画正在播放并且您快速上下滚动(使用视口中的画布)时,您可以看到它减慢了一点点。不知道造成这种情况的原因是,每次元素到达Window
中的某个点时,我只会向动画函数发送1次调用。有什么想法吗?
编辑: 更新了链接,只需向下滚动,通过放慢速度我的意思就更清楚了。
请参阅此fiddle或更低版本中的工作示例。
function createBorderAnimation(elm) {
console.log(elm);
var canvas = elm.get(0),
ctx = canvas.getContext('2d');
canvas.width = $(window).width() / 4 * 3;
canvas.height = $(window).height() / 2;
var Point = function(x, y) {
this.x = x;
this.y = y;
};
var points = [
new Point(0, 0),
new Point(canvas.width, 0),
new Point(canvas.width, canvas.height),
new Point(0, canvas.height),
new Point(0, -10),
];
function calcWaypoints(vertices) {
var waypoints = [];
for (var i = 1; i < vertices.length; i++) {
var pt0 = vertices[i - 1];
var pt1 = vertices[i];
var dx = pt1.x - pt0.x;
var dy = pt1.y - pt0.y;
for (var j = 0; j < 50; j++) {
var x = pt0.x + dx * j / 50;
var y = pt0.y + dy * j / 50;
waypoints.push({
x: x,
y: y
});
}
}
return (waypoints);
}
var wayPoints = calcWaypoints(points);
ctx.strokeStyle = "rgba(0,0,0,0.5)";
ctx.lineWidth = 5;
ctx.moveTo(points[0].x, points[0].y);
ctx.globalCompositeOperation = 'destination-atop';
var counter = 1,
inter = setInterval(function() {
var point = wayPoints[counter++];
ctx.lineTo(point.x, point.y);
ctx.stroke();
if (counter >= wayPoints.length) {
clearInterval(inter);
$(canvas).parent().addClass('active');
}
}, 1);
ctx.stroke();
}
$(window).scroll(function() {
var st = $(window).scrollTop();
$('canvas').each(function(key, elm) {
var _this = $(elm);
if (st > _this.offset().top - $(window).height()) {
if (!_this.hasClass('animating')) {
_this.addClass('animating');
createBorderAnimation(_this);
}
TweenLite.set(_this, {
y: -(st - _this.offset().top) / (1.5 * 10)
});
}
});
});
&#13;
canvas {
width: 35vw;
height: 50vh;
display: inline-block;
vertical-align: top;
}
canvas:first-child {
margin: 0 5vw 0 0;
}
div {
width: 75vw;
height: 50vh;
margin: 100px auto;
font-size: 0;
}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/latest/TweenMax.min.js"></script>
<div>
<canvas></canvas>
<canvas></canvas>
</div>
<div>
<canvas></canvas>
<canvas></canvas>
</div>
<div>
<canvas></canvas>
<canvas></canvas>
</div>
<div>
<canvas></canvas>
<canvas></canvas>
</div>
<div>
<canvas></canvas>
<canvas></canvas>
</div>
&#13;
答案 0 :(得分:1)
你从@ Blindman67和@markE得到了一些很好的建议(他的答案现已删除),关于
setInterval
用于动画我还要补充一点,你应该尽量避免初始化(初始化一次,然后重用),并小心使用jQuery。
关于setInterval
,它只是糟糕,不精确,并且可能导致浏览器在太短的时间间隔内发生崩溃(这只是提醒浏览器它必须在间隔时间后执行某些操作,所以如果某些东西需要比间隔更长的时间,它只会继续添加要做的事情,并会尝试全部执行它们,阻止所有其他资源,最后只是崩溃......)
应该注意的是,滚动事件可能会根据所使用的设备快速启动,并且通常比屏幕刷新率更快,因此它是需要最优化的事件之一。
这里你在scroll事件中广泛使用jQuery的方法。 $(selector)
方法是一种更复杂的方式来调用document.querySelectorAll()
,这本身就是一种嘈杂的操作。
而且,对于所有这些画布,你再次调用jQuery的方法来获得它们的大小和位置。要理解为什么它在高速下很糟糕,请记住.height
每次调用window.getComputedStyle(elem)
,它确实返回分配给元素的每个样式,偶尔可能会很好,但不是在你的情况下(&gt;每秒300次通话)。
一个简单的解决方法:将所有这些调用一次并存储在某处不会改变的内容。
您也可以使用requestAnimationFrame
限制它,这是一个使用标志的简单示例:
// our flag
var scrolling;
window.onscroll=function(){
// only if we haven't already stacked our function
if(!scrolling){
// raise our flag
scrolling = true
requestAnimationFrame(function(){
// add the callback function
doSomething();
// release the flag for next frame
scrolling = false;
})
}
}
所以这里是使用requestAnimationFrame
对代码进行带注释的清理,并避免过多调用jQuery的方法。它当然可以进一步优化和清理,但我希望你能得到主要的想法。
var BorderAnimation = function() {
// a global array for all our animations
var list = [];
// init one animation per canvas
var initAnim = function() {
// our canvas
var c = this;
// our animation object
var a = {};
a.ctx = c.getContext('2d');
// a function to check if our canvas is visible in the screen
a.isVisible = function(min, max) {
return min < c.offsetTop + a.height && max > c.offsetTop;
};
// a trigger
a.play = function() {
// fire only if we're not already animating, and if we're not already finished
if (!a.playing && !a.ended) {
a.playing = true;
loop();
}
};
// reverse trigger
a.pause = function() {
a.playing = false;
}
// our looping function
var loop = function() {
// perform our drawing operations
a.draw(a.ctx);
// and only if we should still be playing...
if (a.playing) {
//...loop
requestAnimationFrame(loop);
}
};
// init our canvas' size and store it in our anim object
a.width = c.width = $(window).width() / 4 * 3;
a.height = c.height = $(window).height() / 2;
// initialize the drawings for this animation
initDrawing(a);
// push our animation in the global array
list.push(a);
};
// this does need to be made outside, but I feel it's cleaner to separate it,
// and if I'm not wrong, it should avoid declaring these functions in the loop.
var initDrawing = function(anim) {
var ctx = anim.ctx;
var Point = function(x, y) {
this.x = x;
this.y = y;
};
var points = [
new Point(0, 0),
new Point(anim.width, 0),
new Point(anim.width, anim.height),
new Point(0, anim.height),
new Point(0, -10),
];
function calcWaypoints(vertices) {
var waypointsList = [];
for (var i = 1; i < vertices.length; i++) {
var pt0 = vertices[i - 1];
var pt1 = vertices[i];
var dx = pt1.x - pt0.x;
var dy = pt1.y - pt0.y;
for (var j = 0; j < 50; j++) {
var x = pt0.x + dx * j / 50;
var y = pt0.y + dy * j / 50;
waypointsList.push({
x: x,
y: y
});
}
}
return (waypointsList);
}
var wayPoints = calcWaypoints(points);
ctx.strokeStyle = "rgba(0,0,0,0.5)";
ctx.lineWidth = 5;
ctx.globalCompositeOperation = 'destination-atop';
// always better to add this, e.g if you plan to make a `reset` function
anim.ctx.beginPath();
anim.ctx.moveTo(points[0].x, points[0].y);
var counter = 1;
// store into our drawing object the drawing operations
anim.draw = function() {
// we reached the end
if (counter >= wayPoints.length) {
anim.playing = false;
anim.ended = true;
return;
}
var point = wayPoints[counter++];
ctx.lineTo(point.x, point.y);
// ctx.clearRect(0,0,anim.width, anim.height); // don't you need it ???
ctx.stroke();
}
};
// a single call to the DOM
$('canvas').each(initAnim);
var scrolling;
var checkPos = function() {
// release the flag
scrolling = false;
var st = pageYOffset; // it's just the same as $(window).scrollTop();
// loop over our list of animations
list.forEach(function(a) {
// the canvas is in the screen
if (a.isVisible(st, st + window.innerHeight)) {
// it will be blocked if already playing
a.play();
} else {
// stop the loop
a.pause();
}
});
};
$(window).scroll(function() {
if (!scrolling) {
// set the flag
scrolling = true;
requestAnimationFrame(checkPos);
}
});
// if you want to do something with these animations, e.g debugging
return list;
}();
canvas {
width: 35vw;
height: 50vh;
display: inline-block;
vertical-align: top;
}
canvas:first-child {
margin: 0 5vw 0 0;
}
div {
width: 75vw;
height: 50vh;
margin: 100px auto;
font-size: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
<canvas></canvas>
<canvas></canvas>
</div>
<div>
<canvas></canvas>
<canvas></canvas>
</div>
<div>
<canvas></canvas>
<canvas></canvas>
</div>
<div>
<canvas></canvas>
<canvas></canvas>
</div>
<div>
<canvas></canvas>
<canvas></canvas>
</div>