十五、使用Canvas绘图
15.1 基本用法
- 初始化
- 设置width和height属性
- <canvas width=‘200’ height=‘200’>…</canvas>
- 开始和结束标签中的内容是后备信息
- 绘图上下文
- 进行绘图的必备操作
- 方式
- 在<canvas>对象上调用getContext()
- 导出<canvas>绘制的图像
15.2 2D上下文
- 使用2D上下文提供的方法,可以绘制简单的2D图像,例如矩形、弧线和路径
15.2.1 填充和描边
15.2.2 绘制矩形
- fillRect(x,y,width,height)
- strokeRect(x,y,width,height)
- clearRect(x,y,width,height)
<html>
<head>
<script>
window.onload=function(){
var drawing = document.getElementById('drawing');
if(drawing.getContext){
var context = drawing.getContext('2d');
context.fillStyle='red';
context.fillRect(10,10,50,50);
context.strokeStyle='blue';
context.strokeRect(30,30,50,50);
context.clearRect(20,20,10,10);
}
};
</script>
</head>
<body>
<canvas id='drawing' width='200' height='200'>不支持canvas</canvas>
</body>
</html>
15.2.3 绘制路径
- beginPath()
- arc(x, y, radius, startAngle, endAngle, counterclockwise)
- arcTo(x1, y1, x2, y2, radius)
- bezierCurveTo(c1x, c1y, c2x, c2y, x, y)
- 起始点:当前位置
- 终点:(x, y)
- 控制点:(c1x, c1y)、(c2x, c2y)
- lineTo(x, y)
- moveTo(x, y)
- quadraticCurveTo(cx, cy, x, y)
- 起始点:当前位置
- 终点:(x, y)
- 控制点:(cx, cy)
- 曲线类型:二次曲线
- rect(x, y, width, height)
- closePath()
- 路径只是路径
- isPointInPath(x, y)
15.2.4 绘制文本
- fillText(text, x, y, maxWidth)
- strokeText(text, x, y, maxWidth)
- 通过上下文设置文本属性
- font
- textAlign:x坐标指代的文本的位置
- textBaseline:y坐标的位置
- measureText()
- 利用font、textAlign、textBaseline计算当前文本大小
- 返回值
15.2.5 变换
- rotate(angle):旋转弧度
- scale(scaleX, scaleY):缩放
- translate(x, y):将(x, y)设置为坐标原点
- transform(m1_1, m1_2, m2_1, m2_2, dx, dy):修改变换矩阵
- setTransform(m1_1, m1_2, m2_1, m2_2, dx, dy)
- 将变化矩阵设置为默认状态,再执行transfrom()
- save()
- restore()
15.2.6 绘制图像
- drawImage( image, x, y, width, height)
- image可以是<image>也可以是<canvas>
- drawImage( image, initialX, initialY, initialWidth, initialHeight, destX, destY, destWidth, destHeigth)
15.2.7 阴影
- 上下文属性
- shadowColor
- shadowOffsetX
- shadowOffsetY
- shadowBlur
15.2.8 渐变
- createLinearGradient(x1, y1, x2, y2)
- addColorStop()
var gradient=context.createLinearGradient(30,30,70,70);
gradient.addColorStop(0,'white');
gradient.addColorStop(1,'black');
context.fillStyle=gradient;
context.fillRect(30,30,50,50);
- createRadialGradient(x1, y1, radius1, x2, y2, radius2)
15.2.9 模式
- createPattern()
- 接收参数
- <img>或<video>或<canvas>
- ‘repeat’、‘repeat-x’、‘repeat-y’、‘no-repeat’
var image=document.images[0];
pattern=conext.createPattern(image, 'repeat');
context.fillStyle=pattern;
context.fillRect(10,10,150,150);
15.2.10 使用图像数据
- context.getImageData(x, y, width, height)
- 返回值为ImageData的实例
- 具有三个属性
- width
- height
- data
- 数组
- 每个像素用数组中的4个元素保存,即r、g、b和透明度
- 可以操作原始像素值实现灰阶过滤
- context.putImageData(imageData, x, y)
15.2.11 合成
- context.globalAlpha
- context.globalCompositionOperation
- 表示后绘制的图形怎样与先绘制的图形结合
- 取值
- source-over:后绘制的图形位于先绘制的图形上方
- source-in:后绘制的图形与先绘制的图形重叠的部分可见,两者其他部分完全透明
- …
15.3 WebGL
15.3.1 类型化数组
- 复杂计算涉及数值精度
- ArrayBuffer
- ArrayBuffer对象表示的只是内存中指定的字节数
- 为将来使用而分配一定数量的字节
- 通过该对象获得的信息只有它包含的字节数
15.3.1.1 视图
- DataView
- 通过它可以选择ArrayBuffer中一小段字节
- 构造函数接收参数
- ArrayBuffer
- byteOffset 字节偏移量
- byteLength 字节数
- 实例属性
- byteOffset
- byteLength
- buffer 取得数组缓冲器ArrayBuffer
- 数据类型及读写方法
| 数据类型 | getter | setter |
|---|
| 有符号8位整数 | getInt8(byteOffset) | setInt8(byteOffset, value) |
| … | … | … |
| 有符号16位整数 | getInt8(byteOffset, littleEndian) | setInt8(byteOffset, value, littleEndian) |
| … | … | … |
var buffer = new ArrayBuffer(20),
view = new DataView(buffer),
value;
view.setUint16(0,25);
view.setUint16(2,50);
value=view.getUint16(0);
15.3.1.2 类型化视图
- 又称为类型化数组,继承DataView,不需要记忆每个类型占用的字节数
- Int8Array
- Uint8Array
- …
- Float64Array
- 构造函数属性 BYTES_PER_ELEMENT属性表示每个元素占用的字节数
var int8s = new Int8Array(buffer, 0, 10 * Int8Array.BYTES_PER_ELEMENT);
var uint16s = new Uint16Array(buffer, int8.byteOffset + int8.byteLength, 5* Uint16Array.BYTES_PER_ELEMENT);
- 也可以不用首先创建ArrayBuffer对象,直接传入希望保存的元素数,会自动创建ArrayBuffer对象
- 可以像访问数组一样访问类型化视图中的元素
- 类型化视图的实例方法 subarray( startElementIndex, endElementIndex )
- 基于底层数组缓冲器的子集创建一个新视图,相当于取数组的一部分生成新数组
- 基于同一个ArrayBuffer
- 操作大数组中的一部分元素时,无需担心意味修改了其他元素
15.3.2 WebGL上下文
- getContext()
- 上下文名称 webgl
- 设置,本身是一个对象,即 { alpha:true }
15.3.2.1 常量
- OpenGL中的类似常量GL_COLOR_BUFFER_BIT在WebGL中为gl.COLOR_BUFFER_BIT
15.3.2.2 方法命名
15.3.2.3 准备绘图
- 操作WebGL上下文之前用某种实色清除<canvas>
gl.clearColor(0,0,0,1);
gl.clear(gl.COLOR_BUFFER_BIT);
15.3.2.4 视口与坐标
- viewport(x, y, width, height)
- 坐标
15.3.2.5 缓冲区
- createBuffer() 创建WebGL缓冲区
- bindBuffer() 将缓冲区绑定到上下文
- bufferData() 写入缓冲区
- drawElements() 输出缓冲区内容
- deleteBuffer() 清空缓冲区
var buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([0, 0.5, 1]), gl.STATIC_DRAW);
15.3.2.6 错误
- WebGL不会抛出错误
- 必须在可能出错的地方手动调用gl.getError()
15.3.2.7 着色器
- 着色器分类
- 着色器语言GLSL(OpenGL Shading Language)
15.3.2.8 编写着色器
- GLSL为类C语言
- 每个着色器都有一个main()方法
- 为着色器传递数据的方式
- Attribute 向顶点着色器传入顶点信息
- Uniform 向任何着色器传入常量值
attribute vec2 aVertexPosition;
void main(){
gl_Position = vec4(aVertexPosition, 0.0 1.0);
}
uniform vec4 uColor;
void main(){
gl_FragColor = uColor;
}
15.3.2.9 编写着色器程序
- 浏览器无法解析GLSL程序,必须准备好字符串形式的GLSL以便编译并链接到着色器程序
- 将GLSL包含在<script>中,并设置自定义的type属性
- 创建着色器对象 gl.createShader()
- 编译着色器 gl.compileShader()
<script type="x-webgl/x-vertex-shader" id ="vertexShader">
attribute vec2 aVertexPosition;
void main(){
gl_Position = vec4(aVertexPosition, 0.0 1.0);
}
</script>
<script type="x-webgl/x-vertex-shader" id ="fragmentShader">
uniform vec4 uColor;
void main(){
gl_FragColor = uColor;
}
</script>
var vertexGlsl = document.getElementById('vertexShader').text,
fragmentGlsl = document.getElementById('fragmentShader').text;
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexGlsl);
gl.compileShader(vertexShader);
var program=gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
15.3.2.10 为着色器传入值
var uColor = gl.getUniformLocation(program, "uColor");
gl.uniform4fv(uColor, [0, 0, 0, 1]);
var aVertexPosition= gl.getAttributeLocation(program, "aVertexPosition");
gl.enableVertexAttribArray(aVertexPosition);
gl.vertexAttribPointer(aVertexPosition,itemSize,gl.FLOAT,false, 0, 0);
15.3.2.11 调试着色器和程序
if(!gl.getShaderParameter(vertexShader,gl.COMPLE_STATUS)){
alert(gl.getShaderInfoLog(vertexShader);
}
if(!gl.getProgramParameter(program,gl.LINK_STATUS)){
alert(gl.getProgramInfoLog(program);
}
15.3.2.12 绘图
- 只能绘制点、线和三角
- gl.drawArrays() 数组缓冲区
- gl.drawElemnts() 元素数组缓冲区
- 接收参数
- 常量,表征绘制形状
- 缓冲区起始索引
- 缓冲区包含的点的集合数
var vertices = new Float32Array([0, 1, 1, -1, -1, -1]),
buffer=gl.createBuffer(),
vertexSetSize=2,
vertexSetCount=vertices.length/vertexSetsize,
uColor,aVertexPosition;
gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
var uColor = gl.getUniformLocation(program, "uColor");
gl.uniform4fv(uColor, [0, 0, 0, 1]);
var aVertexPosition= gl.getAttributeLocation(program, "aVertexPosition");
gl.enableVertexAttribArray(aVertexPosition);
gl.vertexAttribPointer(aVertexPosition,vertexSetSize,gl.FLOAT,false, 0, 0);
gl.drawArray(gl.TRIANGLES,0,vertexSetCount);
15.3.2.13 纹理
var 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);
15.3.2.14 读取像素
- gl.readPixels(x, y, width, height, imageFormat, dataType, ArrayBuffer)
- 从帧缓冲区读取像素并保存到类型化数组中
- 图像格式为gl.RGBA
- 数据类型与ArrayBuffer匹配
- 绘制图像后帧缓冲区会恢复其原始的干净状态
15.3.3 支持