早期基本用 setInterval()来控制动画的执行
(function() {
function updateAnimations() {
doAnimation1();
doAnimation2();
// 其他任务
}
setInterval(updateAnimations,100);
})();
updateAnimations () 会周期性运行注册的动画任务,并反映出每个任务的变化。
无论setInterval()还是setTimeout() 都不能保证时间精度。作为第二个参数的样式只能保证合适会把代码添加到浏览器的任务队列,不能保证添加到队列就会立即运行
用来通知JS代码要执行动画了,接收一个参数,此参数是一个要在重绘屏幕前调用的函数。该函数就是修改DOM样式以反映下一次重绘有何变化的地方。为实现动画循环,可以把多个requestAnimationFrame()调用串联起来。传给requestAnimationFrame()的函数也可以是一个参数,此参数是一个DOMHighResTimeStamp的实例,表示下次重绘的时间。
因为requestAnimationFrame()只会调用一次传入的函数,所以每次更新用户界面时需要再手动调用一次。
requestAnimationFrame()也返回一个请求ID,可以通过cancelAnimationFrame() 取消重绘任务
支持requestAnimationFrame()的浏览器实际上会暴露出作为钩子的回调队列。钩子就是浏览器再执行下一次重绘之前的一个点。这个回调队列是一个可修改的函数列表,包含应该在重绘之前调用的函数。
将回调限制为不超过50毫秒执行一次:
let enable = true;
function expensiveOperation() {
console.log("invoked at",Date.now);
}
window.addEventListener("scroll",() => {
if(enable) {
enable = false;
window.requestAnimationFrame(expensiveOperation);
window.setTimeout(() => enable = true, 50);
}
});
计时器用来限制实际的操作执行间隔,requestAnimationFrame 控制在浏览器的哪个渲染周期中执行
缓动动画就是让元素运动速度有所变化,常见的有让速度慢慢停下来
思路:
function animate(obj, target) {
//先清除以前的定时器,只保留当前的一个定时器执行
clearInterval(obj.timer);
obj.timer = setInterval(function () {
// 步长值写到定时器里面
var step = (target - obj.offsetLeft) / 10;
if (obj.offsetLeft >= target) {
//停止
clearInterval(obj.timer);
}
obj.style.left = obj.offsetLeft + step + 'px';
}, 30);
}
//把步长改成整数,不要出现小数
var step = Math.ceil((target - obj.offsetLeft) / 10);
不修改的话可能因为小数问题导致最终到不了target的位置
向前向后都满足了
function animate(obj, target) {
//先清除以前的定时器,只保留当前的一个定时器执行
clearInterval(obj.timer);
obj.timer = setInterval(function () {
//把步长改成整数,不要出现小数
// var step = Math.ceil((target - obj.offsetLeft) / 10);
var step = (target - obj.offsetLeft) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if (obj.offsetLeft >= target) {
//停止
clearInterval(obj.timer);
}
obj.style.left = obj.offsetLeft + step + 'px';
}, 30);
}
btn800.addEventListenr('click',function() {
animate(span,800,function() {
});//函数作为回调函数
})
function animate(obj, target) {
//先清除以前的定时器,只保留当前的一个定时器执行
clearInterval(obj.timer);
obj.timer = setInterval(function () {
//把步长改成整数,不要出现小数
// var step = Math.ceil((target - obj.offsetLeft) / 10);
var step = (target - obj.offsetLeft) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if (obj.offsetLeft >= target) {
//停止
clearInterval(obj.timer);
// 回调函数写到定时器结束里面
if(callback) {
//调用函数
callback();
}
}
obj.style.left = obj.offsetLeft + step + 'px';
}, 30);
}
绘制元素至少要设置width和height属性
<canvas id="drawing" width="200" height="200">hhhcanvas>
注意:width 和 height 没有单位
在画布上绘制图形,首先要取得绘画上下文。使用getContext()获取绘画上下文的引用。对于平面图形,需要给该方法传入参数“2d”,表示要获取的2d上下文对象
let drawing = document.getElementById("drawing");
// 确保浏览器支持canvas
if (drawing.getContext) {
let context = drawing.getContext('2d');
//...
}
可使用该方法导出canvas元素上的图像,接收一个参数:要生成图像的MIME类型
2D上下文的坐标原点(0,0)在 元素的左上角。
这两个属性可以是字符串、渐进对象或图案对象,默认值为“#000000”。
let drawing = document.getElementById("drawing");
// 确保浏览器支持canvas
if (drawing.getContext) {
let context = drawing.getContext('2d');
context.strokeStyle = "red";
context.fillStyle = "#0000ff";
}
方法有:
都接收4个参数:矩形x坐标、矩形y坐标、矩形宽度和矩形高度。单位都是像素
接收x轴和y轴坐标作为参数。该方法用于确定指定的点是否在路径上,可以在关闭路径前随时调用
方法:fillText()和 strokeText(),都接收4个参数:要绘制的字符串、x坐标、y坐标和可选的最大像素宽度,4个属性如下:
接收一个参数,即要绘制的文本,然后返回一个 TextMetrics 对象。返回的这个对象只有一个属性width,measureText()方法使用 font、textAlign 和 textBaseline 属性当前的值计算绘制指定文本的大小
例子:假设把文本“Hello world!”放到一个140像素框的矩形中,可从100像素的字体大小开始计算,不断递减,直到文本大小合适:
let fontSize = 100;
context.font = fontSize + 'px Arial';
while(context.measureText('Hello world!').width > 140) {
fontSize--;
context.font = fontSize + 'px Arial';
}
context.fillText('Hello world!',10,10);
context.fillText('Font size is' + fontSize + 'px',10,50);
用于改变绘制上下文的变换矩阵:
若想把现有的图像绘制到画布上,可使用drawImage()。
传入一个HTML的元素、绘制目标的x和y坐标,结果:把图像绘制到指定位置
let image = document.images[0];
context.drawImage(image,10,10);
可改变所绘制图像的大小
传入一个HTML的元素、绘制目标的x和y坐标,目标宽度和目标高度。这里的缩放只影响绘制的图像,不影响上下文的变换矩阵:
context.drawImage(image,50,10,20,30);
图像缩放到20像素宽、30像素高
要绘制的图像,源图像x坐标,源图像y坐标,源图像宽度,源图像高度,目标区域x坐标,目标区域y坐标、目标区域宽度和目标区域高度
context.drawImage(image,0,10,50,50,0,100,40,60);
从(0,10)开始,50像素宽、50像素高。而绘制到画布上是,会从(0,100)开始,变成40像素宽,60像素高
属性:
步骤:
let gradient = context.createLinearGradient(30,30,70,70);
gradient.addColorStop(0,'white');
gradient.addColorStop(1,'black');
使用 **createRadialGradient()**创建,接收6个参数:分别对应两个圆形圆心的坐标和半径
图案用于填充和描画图形的重复图像。创建新图案,可以调用 createPattern()并传入两个参数:(1)一个元素 (2)一个表示该如何重复图像的字符串。第二个参数的值与CSS的background-repeat属性一样
使用 getImageData() 获取原始图像数据。接收4个参数:要取得数据中第一个像素的左上角坐标和要取得的像素宽度及高度。 返回对象是ImageData的实例,每个对象包含3个属性:width、height和data。 data属性是包含图像的原始像素信息的数组。每个像素在data数组中都由4个值表示,分别代表红、绿、蓝和透明度值
2D上下文绘制的所有内容都会应用两个属性:globalAlpha 和 globalCompositionOperation。
画布的3D上下文,WebGL是OpenGL ES 2.0 的Web版
在使用上下文之前,应该先检测返回值是否存在:
let drawing = document.getElementById("drawing");
// 确保浏览器支持canvas
if (drawing.getContext) {
let gl = drawing.getContext("webgl");
if(gl) {
//使用webgl
}
}
可在getContext()取得WebGL上下文时指定一些选项。这些选项通过一个参数对象传入,选项就是参数对象的一个或多个属性
let drawing = document.getElementById("drawing");
// 确保浏览器支持canvas
if (drawing.getContext) {
try{
let gl = drawing.getContext("webgl",{
alpha:false});
} catch(ex){
}
if(gl) {
//使用webgl
}
}
在OpenGL中常量以GL开头,在WebGL要换种方法访问,如:GL_COLOR_BUFFER_BIT 在WebGL中要访问 g.COLOR_BUFFER_BIT
准备使用WebGL上下文之前,通常需要先指定一种实心颜色清除。为此需要调用clearColor()并传入4个参数,分别表示红、绿、蓝和透明值,参数范围都是(0~1范围),表示各个组件在最终颜色的强度
gl.clearColor(0,0,0,1);
gl.clear(gl.COLOR_BUFFER_BIT);
要改变视口,可调用 viewport()并传入视口相对于 元素事务x、y坐标及宽高度。如果绘图时用了视口外部的坐标,则绘制结果会被视口剪切
视口的 x 和 y坐标起点(0,0)表示的左下角,向上,向右增长可以用点(width-1,height-1)定义:
例子:定义视口是左上角四分之一区域:
gl.viewport(0,drawing.height/2,drawing.width/2,drawing.height/2);
在 JS 中,顶点信息保存在定性数组中。要使用这些信息,必须先将他们转换为WebGL缓冲区。创建缓冲区要调用 gl.createBuffer(),并使用 **gl.bindBuffer()**将缓冲区绑定到WebGL上下文,绑定之后就可以使用数据填充缓冲区了,如:
let buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array([0,0.5,1]),gl.STATIC_DRAW);
gl.bufferData() 方法的最后一个参数表示如何使用缓冲区
不在使用缓冲区时,调用 gl.deleteBuffer() 释放其占用的内存
WebGL中通常不会抛出错误**,必须调用可能失败的方法后,调用 gl.getError()**,该方法返回一个常量,表示发生的错误类型:
WebGL 中有两种着色器:顶点着色器和片段(或像素)着色器,WebGL是使用GLSL语言实现的
每个着色器都有一个 **main()**方法,在绘制期间会重复执行。给着色器传递参数的方式有两种:attribute 和 uniform。attribute :用于将顶点传入顶点着色器,uniform:用于将常量值传入任何着色器。。attribute 和 uniform 是在main()函数外部定义的。在值类型关键字之后是数据类型,然后是变量名
attribute vec2 aVertexPosition;
void main() {
gl_Position = vec4(aVertexPosition,0.0,1.0);
}
定义了名为aVertexPosition的attribute。该attribute是一个包含两项的数组(数据类型为vec2),代表x和y坐标
WebGL1 几乎与 WebGL12兼容,在WebGL12 中很多功能变成了默认功能,以下特性都已经成为WebGL12的标准特性: