当前位置: 首页 > news >正文

十八、动画与canvas

在这里插入图片描述

1.RequestAnimationFrame

早期定时动画

setTimeout和setInterval不能保证时间精度,第二个参数只能保证何时将代码添加到浏览器的任务队列
requestAnimationFrame(cb)的cb在浏览器重绘屏幕前调用

function updateProgress(){const div = document.getElementById('div');div.style.width = (parseInt(div.style.width, 10) + 5) + '%';if (div.style.width !== '100%'){requestAnimationFrame(updateProgress);}
}
requestAnimationFrame(updateProgress);

requestAnimationFrame()的函数实际上可以接受一个参数,该参数为DOMHighResTimeStamp的实例,表示下次重绘的时间点。

cancelAnimationFrame(id); // 对应clearTimeout clearInterval

requestAnimationFrame()可以用作节流

let enabled = true;
function expensiveOperation(){console.log('scroll', Date.now());
}
window.addEventListener('scroll', () => {if (enabled) {enabled = false;window.requestAnimationFrame(expensiveOperation);setTimeout(() => enabled = true, 50)}
}

2.基本的画布功能

<canvas>元素必须要指定width和height两个属性,标签之间的内容是在浏览器不支持的情况下展示的内容

getContext();// 获取绘制上下文,参数为‘2d’或者‘webgl’
toDataURL(); // 导出canvas元素上的图像,接受一个MIME类型参数

3、2D绘图上下文

原点(0,0)在左上角,width和height分别为x轴和y轴的最大值

填充和描边

fillStyle和strokeStyle两个属性,属性值可以是字符串、渐变对象和图案对象

const myCanvas = document.getElementById('myCanvas');
const ctx = myCanvas.getContext('2d');
ctx.strokeStyle = 'red';
ctx.fillStyle = '#0000ff';

绘制矩形

fillRect(x,y, width, height);
strokeRect(x, y, width, height);
clearRect(x, y, width, height);

绘制路径

必须beginPath()开始绘制新路径,closePath()绘制一条返回起点的路径。而后可以指定fillStyle属性后,使用fill()填充区域,或者指定strokeStyle属性后,使用stroke()填充路径,或者使用clip()创建一个新的剪切区域。

方法说明
arc(x, y, startAngle, endAngle, counterclockwise)以(x,y)为圆心,从起始角度到结束角度绘制弧线,最后一个参数为是否顺时针,默认为true
arcTo(x1, y1, x2, y2, radius)给定半径,经过(x1, y1)从上一个点绘制到(x2,y2)的弧线
bezierCurveTo(c1x, c1y, c2x, c2y, x, y)以(c1x, c1y)(c2x, c2y)为控制点,绘制从上一点到(x,y)的三次贝塞尔曲线
lineTo(x,y)上一点到(x,y)的直线
moveTo(x,y)移动光标到(x,y)
quadraticCurveTo(cx, cy)以(cx, cy)为控制点,绘制从上一点到(x,y)的二次贝塞尔曲线
rect(x, y, width, height)绘制一个矩形路径,而非图形
isPointInPath(x, y)判断(x, y)是否在当前路径

绘制文本

fillText(contentStr, x, y, maxWidth);
strokeText(contentStr, x, y, maxWidth);
// 相关设置属性
ctx.font; // 以css样式指定字体样式、大小、字体簇
ctx.textAlign; // 文本对齐方式 start/end/left/right/center
ctx.textBaseLine; // 文本基线, top/hanging/middle/alphabetic/ideographic/bottom

辅助确定文本大小的方法measureText(str),返回一个TextMetrics对象

变换

方法
rotate(angle)围绕原点将图像旋转angle
scale(scaleX, scaleY)在x轴和y轴上分别缩放相应倍数
translate(x, y)将坐标原点移动到(x, y)
transform(m1_1, m1_2, m2_1, m2_2, dx, dy)通过矩阵乘法直接修改上下文矩阵
m1_1m2_1dx
m1_2m2_2dy
001
setTransform(m1_1, m1_2, m2_1, m2_2, dx, dy)先将上下文变换矩阵重置,然后以传入的参数调用transform

save()可以将所有设置放入一个暂存栈,需要恢复时调用restore()

绘制图像

drawImage()可以接受三组不同的参数

//1.传入一个HTML的<img>元素
let img = document.images[0];
ctx.drawImage(img, x, y);
ctx.drawImage(img, x, y, width, height); // width和height默认为<img>的宽高
// 2.将图像的部分绘制到指定区域
ctx.drawImage(img, srcX, srcY, srcWidth, srcHeight, targetX, targetY, targetWidth, targetHeight);
// 3.第一个参数可以是另外一个canvas元素
// 操作的结果可以通过toDataURL() 获取

阴影

属性值设置

属性说明
shadowColorCSS颜色值,默认黑色
shadowOffsetXx坐标偏移量
shadowOffsetYy坐标偏移量
shadowBlur阴影模糊量

渐变

线性渐变通过CanvasGradient的实例表示,createLinearGradient()创建一个CanvasGradient实例,接受起点和终点的坐标作为参数;
addColorStop()方法为gradient对象添加色标,接受两个参数(位置0-1,颜色)

const gradient = ctx.createLinearGradient(30,30,70,70);
gradient.addColorStop(0, 'white');
gradient.addColorStop(1, 'black');
// 绘制红色矩形
ctx.fillStyle = '#FF0000';
ctx.fillRect(10,10,50,50);
// 绘制渐变矩形
ctx.fillStyle = gradient;
ctx.fillRect(30,30,50,50);

径向渐变通过createRadialGradient()创建,接受六个参数,分别为起点和终点的圆心坐标、半径、

ctx.createRadialGradient(startX, startY, startR, endX, endY, endR);

图案

用于填充和描画重复图案,调用createPattern(),接受两个参数,第一个参数为<img>或者<video> 和<canvas>,第二个参数为重复模式(repeat-x, repeat-y, repeat, no-repeat)

图像数据

getImageData(startX, startY, width, height)返回一个ImageData对象,每个ImageData包含三个属性width, height, data,其中data中每四个值分别表示第i个像素点的红、绿、蓝和透明度值,取值范围为0-255

合成

属性值说明
globalAlpha全局透明度,0-1, 用于指定绘制内容的透明度
globalCompositionOperation新绘制的形状如何与已有的上下文形状融合
source-over新图形在原图形上面,默认值
source-in只画出新图形与原图形重叠的部分,画布上其他部分全部透明
source-out只画出新图形不重叠的部分,画布上其他部分全部透明
source-atop只画出新图形重叠部分,其他部分不受影响
destination-over新图形在原图形下面,重叠部分只有原图形透明像素下可见
destination-in新图形在原图形下面,画布上只留下两者重叠部分,其他部分全部透明
destination-out新图形与原图形重叠部分透明,不重叠部分不画,画布其他部分不受影响
destination-atop新图形在原图形下面,原图形不重叠部分全部透明
lighter新图形与原图形重叠部分像素值相加,让该部分变亮
copy新图形将擦除并完全取代原图形
xor新图形与原图形在重叠部分取“异或”操作

4.WebGL

webgl上下文

getContext(‘webgl’);

webgl基础

可以在调用getContext()时传入配置项,指定创建的上下文的一些选项

配置项说明默认值
alpha是否为上下文创建透明通道缓冲区true
depth是否使用16位深缓冲区true
stencil是否使用8位模板缓冲区false
antialias是否使用默认机制执行抗锯齿操作true
premultipliedAlpha绘图缓冲区是否预乘透明度true
preserveDrawingBuffer绘图完成后是否保留绘图缓冲区false
let drawing = document.getElementById("drawing");
// 确保浏览器支持<canvas>
if (drawing.getContext) {let gl = drawing.getContext("webgl", { alpha: false });if (gl) {// 使用 WebGL}
} 
1.常量

OpenGL中以GL_开头的常量大部分都被webGL支持,但不需要这个前缀

gl.COLOR_BUFFER_BIT // GL_COLOR_BUFFER_BIT
2.方法命名

参数数量(1-4)+数据类型(‘f’, ‘i’)如, gl.uniform4f()

3.准备绘图

绘图之前,需要使用clearColor(r, g, b, a) 使用一种颜色清除<canvas>元素, rgba参数值都在0-1;
也可以使用gl.clear(gl.COLOR_BUFFER_BIT)使用之前定义的颜色填充画图

4.视口与坐标

默认情况下,视口为整个canvas区域,也可以使用viewport()方法改变视口

gl.viewport(x, y, width, height)

定义视口的坐标系统是canvas像素坐标系统,而视口中,中心点是原点(0,0),左下角是(-1,-1),右上角是(1,1)。

5.缓冲区
const buffer = gl.createBuffer(); // 创建缓冲区
gl.bindBuffer(gl.ARRAY_BUFFER, buffer); // 将缓冲区绑定到上下文中,此处是将buffer与ARRAY_BUFFER绑定,将buffer设置为当前上下文的缓冲区
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0,0.5,1]), gl.STATIC_DRAW); // 将数据写入缓冲区第三个参数可以有3个选择
/**
gl.STATIC_DRAW  数据加载一次,可以在多次绘制中使用
gl.STREAM_DRAW 数据加载一次,只能在几次绘制中使用
gl.DYNAMIC_DRAW  数据可以重复修改,在多次绘制中使用
**/
// 不再需要缓冲区
gl.deleteBuffer(buffer);
6.错误

webGL操作中通常不会抛出错误,gl.getError()方法会返回一个常量,表示发生的错误类型

gl.NO_ERROR没有发生错误
gl.INVALID_ENUM上一次操作没有传入webgl预定的常量
gl.INVALID_VALUE上一次操作需要无符号数值,但是传入了负数
gl.INVALID_OPERATION上一次操作在当前状态下无法完成
gl.OUT_OF_MEMORY上次操作因内存不足无法完成
gl.CONTEXT_LOST_WEBGL上次操作因外部事件而丢失了上下文
7.着色器

定点着色器将3D顶点绘制为2D的点;
片元着色器将计算每个像素的颜色值;
使用GLSL编程

编写着色器

每个着色器有一个main()方法,绘制期间反复执行,
给着色器传递数据的方式有两种attributeuniform
attribute将定点传入定点着色器,uniform将任意常量值传入任意着色器

创建着色器程序
gl.createShader(shaderType); // shaderType: gl.VERTEX_SHADER, gl.FRAGMENT_SHADER 创建着色器
gl.shaderSource(shader, shaderSource);// 将glsl代码应用于着色器
gl.compileShader(shader); // 编译着色器const program = gl.createProgram(); // 创建着色器程序
gl.attachShader(program, shader); // 将着色器添加到着色器程序,需要调用两次分别传入定点着色器和片元着色器
gl.linkProgram(program); // 将两个着色器链接到变量program
gl.useProgram(program); // 让webgl上下文使用该程序
给着色器传值
// 1.找到接受值的变量位置
let uColorLocation = gl.getUniformLocation(program, 'uColor'); // 针对uniform变量uColor
let aVertextPosLocation = gl.getAttribLocation(program, 'aVertextPosition'); //  针对attribute变量aVertexPosition
// 2.传值
// 针对uniform变量
gl.uniform4fv(uColorLocation, [0,0,0,1]);
// 针对attribte变量
gl.enableVertexAttribArray(aVertexPosLocation); 
gl.vertexAttribPointer(aVertexPosLocation, itemSize, gl.FLOAT, false, 0, 0) ; // 创建一个指向bindBuffer()指定的缓冲区指针,并保存在aVertexPosLocation中,
8.绘图

webGL只能绘制3中基本图形:点、线、三角形
绘图方法有两种:
drawArrays()使用数组缓冲区
drawElements()操作元素数组缓冲区
drawArrays()接受三个参数,第一个参数表示形状,第二个参数是数组缓冲区的起点索引,第三个参数是数组缓冲区顶点集合的数量
形状参数列表:

参数意义
gl.POINTS将每个顶点当做一个点
gl.LINES将数组作为一系列点,每两个点依次绘制直线,顶点数必须是偶数
gl.LINE_LOOP将数组作为一系列顶点,依次依次绘制直线连接成封闭形状
gl.LINE_STRIP类似于gl.LINE_LOOP,区别在于最后不会连接终点和起点
gl.TRIANGLES将数组作为一系列顶点,在这些顶点间绘制三角形,每个三角形都不会共享顶点
gl.TRIANGLES_STRIP类似于gl.TRIANGLES,区别在于共享点,如果有ABCD四个点,则会绘制两个三角形ABC和BCD
gl.TRIANGLES_FAN如果有ABCD四个点,则会绘制ABC, ACD三角形
/****** 绘制一个三角形 ******/const canvas = document.querySelector('#canvas');const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');const vSource = `attribute vec4 a_position;void main(){gl_Position = a_position;}`;const fSource = `precision highp float;uniform vec4 uColor;void main(){gl_FragColor = uColor;}`;const program = gl.createProgram();const vShader = gl.createShader(gl.VERTEX_SHADER);gl.shaderSource(vShader, vSource);gl.compileShader(vShader);const fShader = gl.createShader(gl.FRAGMENT_SHADER);gl.shaderSource(fShader, fSource);gl.compileShader(fShader);gl.attachShader(program, vShader);gl.attachShader(program, fShader);gl.linkProgram(program);gl.useProgram(program);const uColorLocation = gl.getUniformLocation(program, 'uColor');gl.uniform4f(uColorLocation, 1, 0, 0, 0.5);const positionData = [0.5, 0, 0,  0, 0.5, 0, -0.5, -0.5, 0.5];const positionBuffer = gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positionData), gl.STATIC_DRAW);const positionLocation = gl.getAttribLocation(program, 'a_position');gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 0, 0);gl.enableVertexAttribArray(positionLocation);gl.clearColor(0,1,1,1);gl.clear(gl.COLOR_BUFFER_BIT)gl.drawArrays(gl.TRIANGLES, 0, 3);
纹理

纹理的来源可以是图片、<video>或者<canvas>

let image = new Image(),texture;
image.src = "smile.gif";
image.onload = function() {texture = gl.createTexture(); // 创建纹理gl.bindTexture(gl.TEXTURE_2D, texture); // 绑定缓冲区gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); // 设置存储格式gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);// 除当前纹理gl.bindTexture(gl.TEXTURE_2D, null);
} 
读取像素
// 读取(0,0)到(25,25)区域的像素,并写入pixels数组,rgba四值,0-255
let pixels = new Uint8Array(25*25);
gl.readPixels(0, 0, 25, 25, gl.RGBA, gl.UNSIGNED_BYTE, pixels); 
http://www.lryc.cn/news/40016.html

相关文章:

  • 自动化测试学习-Day4-selenium的安装和8种定位方法
  • 【Kubernetes】第二十五篇 - 布署 nodejs 后端项目(下)
  • 贪心算法之区间问题总结
  • 无线WiFi安全渗透与攻防(七)之WIFI07-WEP-wifite自动化渗透WEP加密
  • 震撼,支持多模态模型的ChatGPT 4.0发布了
  • IDEA常用插件列表
  • 比df更好用的命令!
  • 【Git使用学习】记录学习过程(1)
  • K_A18_001 基于STM32等单片机采集MQ2传感参数串口与OLED0.96双显示
  • 【云原生·Docker】常用命令
  • 户外露营储能电源芯片CSU3AF10
  • 无线WiFi安全渗透与攻防(八)之WEP-Hirte渗透WEP加密
  • 前端常考面试题整理
  • 二十二、身份验证与权限
  • k8s pod 升级与回滚
  • 【Go】Go语言开发环境安装
  • el-switch使用
  • 【算法入门】字符串基础
  • 前端面试题 —— 浏览器原理(二)
  • 对于植物神经紊乱的治疗 中医采用辩证论治的方法
  • chatGPT之Python API启用上下文管理
  • 油田钻井实时在线监测系统
  • 经典PID控制算法原理以及优化思路
  • 经典面试题之赋值和深浅拷贝的区别
  • 电子取证的电脑配置有关问题,以我仅有的知识为大家建议一下。
  • 【基础算法】单链表的OJ练习(5) # 环形链表 # 环形链表II # 对环形链表II的解法给出证明(面试常问到)
  • MySQL 基础教程[13]
  • 个人练习-Leetcode-826. Most Profit Assigning Work
  • 云原生周刊:边缘计算会吞噬云吗?| 2023.3.13
  • python+django+vue图书个性化推荐系统