JavaScript 高级程序设计(第15章 使用 Canvas 绘图)

第15章 使用 Canvas 绘图

1. 基本用法

(1) 要使用<canvas>元素,必须先设置其widthheight 属性,指定可以绘图的区域大小。出现在 开始和结束标签中的内容是后备信息,如果浏览器不支持<canvas>元素,就会显示这些信息。

<canvas id="drawing" width=" 200" height="200">A drawing of something.</canvas>

(2) <canvas>元素对应的 DOM 元素对象也有 width 和 height 属性,可以随意修 改。而且,也能通过 CSS 为该元素添加样式,如果不添加任何样式或者不绘制任何图形,在页面中是看 不到该元素的。

(3)getContext()方法:取得绘图上下文对象的引用,传入上下文的名字。传入"2d",就可以取得 2D 上下文对象。

var drawing = document.getElementById("drawing");
//确定浏览器支持<canvas>元素 
if (drawing.getContext){
    var context = drawing.getContext("2d");
     //更多代码
}

(4) toDataURL()方法可以导出在<canvas>元素上绘制的图像。这个方法接受一个参数,即图 像的 MIME 类型格式,而且适合用于创建图像的任何上下文。

var drawing = document.getElementById("drawing");
//确定浏览器支持<canvas>元素
 if (drawing.getContext){
//取得图像的数据 URI
var imgURI = drawing.toDataURL("image/png");
//显示图像
var image = document.createElement("img"); 
image.src = imgURI; document.body.appendChild(image);
}

2. 2D 上下文

(1) 填充和描边

填充,就是用指定的样式(颜色、渐变或图像)填
充图形。fillStyle 属性
描边,就是只在图形的边缘画线。strokeStyle属性

var drawing = document.getElementById("drawing");
//确定浏览器支持<canvas>元素
 if (drawing.getContext){
    var context = drawing.getContext("2d");
    context.strokeStyle = "red";
    context.fillStyle = "#0000ff";
}

(2) 绘制矩形

与矩形有关的方法包括fillRect()strokeRect()clearRect()。这三个方法都能接收 4 个参数:矩形的 x 坐标矩形的 y 坐标矩形宽度矩形高度。这些参数的单位都是像素。

fillRect()方法在画布上绘制的矩形会填充指定的颜色。填充的颜色通过 fillStyle 属性指定。

var drawing = document.getElementById("drawing");
//确定浏览器支持<canvas>元素 
if (drawing.getContext){
    var context = drawing.getContext("2d");

//绘制红色矩形
context.fillStyle = "#ff0000"; context.fillRect(10, 10, 50, 50);
//绘制半透明的蓝色矩形
context.fillStyle = "rgba(0,0,255,0.5)"; context.fillRect(30, 30, 50, 50);
}

strokeRect()方法在画布上绘制的矩形会使用指定的颜色描边。描边颜色通过 strokeStyle 属性指定。

描边线条的宽度由 lineWidth 属性控制,该属性的值可以是任意整数。另外, 通过 lineCap 属性可以控制线条末端的形状是平头、圆头还是方头("butt"、 "round"或"square"),通过 lineJoin 属性可以控制线条相交的方式是圆交、斜 交还是斜接("round"、"bevel"或"miter")。

var drawing = document.getElementById("drawing");
//确定浏览器支持<canvas>元素 
if (drawing.getContext){
        var context = drawing.getContext("2d");
//绘制红色描边矩形
context.strokeStyle = "#ff0000"; context.strokeRect(10, 10, 50, 50);
//绘制半透明的蓝色描边矩形
context.strokeStyle = "rgba(0,0,255,0.5)"; context.strokeRect(30, 30, 50, 50);
}


clearRect()方法用于清除画布上的矩形区域。本质上,这个方法可以把绘制上下文中的某 一矩形区域变透明。

var drawing = document.getElementById("drawing");
//确定浏览器支持<canvas>元素 
if (drawing.getContext){
        var context = drawing.getContext("2d");
        //绘制红色矩形
        context.fillStyle = "#ff0000"; context.fillRect(10, 10, 50, 50);
        //绘制半透明的蓝色矩形
        context.fillStyle = "rgba(0,0,255,0.5)"; context.fillRect(30, 30, 50, 50);
        //在两个矩形重叠的地方清除一个小矩形
        context.clearRect(40, 40, 10, 10);
}

(3) 绘制路径

(1) 要绘制路径,首先必须调用 beginPath()方法,表示要开始 绘制新路径。
(2) 然后,再通过调用下列方法来实际地绘制路径。

  1. arc(x, y, radius, startAngle, endAngle, counterclockwise):以(x,y)为圆心绘 制一条弧线,弧线半径为 radius,起始和结束角度(用弧度表示)分别为 startAngle 和 endAngle。最后一个参数表示 startAngle 和 endAngle 是否按逆时针方向计算,值为 false 表示按顺时针方向计算。
  2. arcTo(x1, y1, x2, y2, radius):从上一点开始绘制一条弧线,到(x2,y2)为止,并且以 给定的半径 radius 穿过(x1,y1)。
  3. bezierCurveTo(c1x, c1y, c2x, c2y, x, y):从上一点开始绘制一条曲线,到(x,y)为 止,并且以(c1x,c1y)和(c2x,c2y)为控制点。
  4. lineTo(x, y):从上一点开始绘制一条直线,到(x,y)为止。
  5. moveTo(x, y):将绘图游标移动到(x,y),不画线。
  6. quadraticCurveTo(cx, cy, x, y):从上一点开始绘制一条二次曲线,到(x,y)为止,并且以(cx,cy)作为控制点。
  7. rect(x, y, width, height):从点(x,y)开始绘制一个矩形,宽度和高度分别由 width 和height 指定。这个方法绘制的是矩形路径,而不是 strokeRect()和 fillRect()所绘制的独立的形状。

(4) 创建了路径后,接下来有几种可能的选择。

  1. closePath():绘制一条连接到路径起点的线条。
  2. fill():填充路径,使用fillStyle 填充。
  3. stroke():对路径描边,描边使用的是 strokeStyle。
  4. clip():在路径上创建一个剪切区域。
var drawing = document.getElementById("drawing");
//确定浏览器支持<canvas>元素
 if (drawing.getContext){
    var context = drawing.getContext("2d");
    //开始路径 
    context.beginPath();
    //绘制外圆
    context.arc(100, 100, 99, 0, 2 * Math.PI, false);
    //绘制内圆
    context.moveTo(194, 100);
    context.arc(100, 100, 94, 0, 2 * Math.PI, false);
    //绘制分针 
    context.moveTo(100, 100); 
    context.lineTo(100, 15);
    //绘制时针 
    context.moveTo(100, 100); 
    context.lineTo(35, 100);
    //描边路径
    context.stroke();
}

isPointInPath()的方法,这个方法接收 x 和 y 坐标作为 参数,用于在路径被关闭之前确定画布上的某一点是否位于路径上。

if (context.isPointInPath(100, 100)){
        alert("Point (100, 100) is in the path.");
}

(4) 绘制文本

  1. 绘制文本主要有两个 方法:fillText()strokeText()
  2. 这两个方法都可以接收4 个参数:要绘制的文本字符串、x 坐 标、y 坐标和可选的最大像素宽度。
  3. 这两个方法都以下列 3 个属性为基础。

(1) font:表示文本样式、大小及字体,用 CSS 中指定字体的格式来指定,例如"10px Arial"。
(2) textAlign:表示文本对齐方式。可能的值有"start"、"end"、"left"、"right"和"center"。
(3) textBaseline:表示文本的基线。可能的值有"top"、"hanging"、"middle"、"alphabetic"、
"ideographic"和"bottom"。

textAlign 设置为"start",则 x 坐标表示的是文本左 端的位置(从左到右阅读的语言);设置为"end",则 x 坐标表示的是文本右端的位置。`

//正常
context.font = "bold 14px Arial";
context.textAlign = "center"; 
context.textBaseline = "middle"; 
context.fillText("12", 100, 20);
//起点对齐
context.textAlign = "start";
 context.fi llText("12", 100, 40);
//终点对齐
context.textAlign = "end";
 context.fi llText("12", 100, 60);

textBaseline 属性的值可以调整文本的垂直对齐方式:值为"top",y 坐标表示文本顶端;值为 "bottom",y 坐标表示文本底端;值为"hanging"、"alphabetic"和"ideographic",则 y 坐标分 别指向字体的特定基线坐标。

measureText()方法利用 font、textAlign 和 textBaseline 的当前值计算指定文本的大小
这个方法接收一个参数,即要绘制的文本;返回一个 TextMetrics 对象。返回的对象目前只有一个width 属性,但将来还会增加更多度量属性。

var 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);

(5) 变换

  1. rotate(angle):围绕原点旋转图像 angle 弧度。
  2. scale(scaleX, scaleY):缩放图像,在 x 方向乘以 scaleX,在 y 方向乘以 scaleY。scaleX和 scaleY 的默认值都是 1.0。
  3. translate(x,y):将坐标原点移动到(x,y)。执行这个变换之后,坐标(0,0)会变成之前由(x,y) 表示的点。
  4. transform(m1_1, m1_2, m2_1, m2_2, dx, dy):直接修改变换矩阵,方式是乘以如下矩阵。


  5. setTransform(m1_1, m1_2, m2_1, m2_2, dx, dy):将变换矩阵重置为默认状态,然后 再调用 transform()。
var drawing = document.getElementById("drawing");
//确定浏览器支持<canvas>元素 
if (drawing.getContext){
        var context = drawing.getContext("2d");
        //开始路径 
        context.beginPath();
        //绘制外圆
        context.arc(100, 100, 99, 0, 2 * Math.PI, false);
        //绘制内圆
        context.moveTo(194, 100);
        context.arc(100, 100, 94, 0, 2 * Math.PI, false);
        //变换原点 
        context.translate(100, 100);
        //旋转表针
        context.rotate(1);
        //绘制分针
        context.moveTo(0,0);
        context.lineTo(0, -85);
        //绘制时针 
        context.moveTo(0, 0); 
        context.lineTo(-65, 0);
        //描边路径
        context.stroke();
}

save()方法 调用这个方法后,当时的所有设置都会进入一个栈结构,得以妥善保管。
restore()方法,在保存设置的栈结构中向前返回一级, 恢复之前的状态。

context.fillStyle = "#ff0000";
context.save();
context.fillStyle = "#00ff00";
context.translate(100, 100);
context.save();
context.fillStyle = "#0000ff";
context.fillRect(0, 0, 100, 200); //从点(100,100)开始绘制蓝色矩形
context.restore();
context.fillRect(10, 10, 100, 200); //从点(110,110)开始绘制绿色矩形
context.restore();
context.fillRect(0, 0, 100, 200); //从点(0,0)开始绘制红色矩形

(6) 绘制图像

drawImage() 方法:想把一幅图像绘制到画布上。

调用这个方法的3种参数组合:

  • 传入一个 HTML <img>元素,以及绘制该图像的起点的 x 和 y 坐标。
var image = document.images[0];
context.drawImage(image, 10, 10);
  • 如果你想改变绘制后图像的大小,可以再多传入两个参数,分别表示目标宽度目标高度
context.drawImage(image, 50, 10, 20, 30);
  • 选择把图像中的某个区域绘制到上下文中。drawImage()方法的这种调 用方式总共需要传入 9 个参数:要绘制的图像、源图像的 x 坐标、源图像的 y 坐标、源图像的宽度、源 图像的高度、目标图像的 x 坐标、目标图像的 y 坐标、目标图像的宽度、目标图像的高度。
context.drawImage(image, 0, 10, 50, 50, 0, 100, 40, 60);

(7) 阴影

  1. shadowColor:用 CSS 颜色格式表示的阴影颜色,默认为黑色。
  2. shadowOffsetX:形状或路径 x 轴方向的阴影偏移量,默认为 0。
  3. shadowOffsetY:形状或路径 y 轴方向的阴影偏移量,默认为 0。
  4. shadowBlur:模糊的像素数,默认 0,即不模糊。

这些属性都可以通过 context 对象来修改。只要在绘制前为它们设置适当的值,就能自动产生阴 影。

var context = drawing.getContext("2d");
//设置阴影
context.shadowOffsetX = 5; 
context.shadowOffsetY = 5;
context.shadowBlur = 4;
 context.shadowColor = "rgba(0, 0, 0, 0.5)";
//绘制红色矩形
context.fillStyle = "#ff0000";
context.fillRect(10, 10, 50, 50);
//绘制蓝色矩形
context.fillStyle = "rgba(0,0,255,1)";
context.fillRect(30, 30, 50, 50);

(8) 渐变

  1. createLinearGradient()方法(线性渐变),这个方法接收 4 个参数:起点的 x 坐标、起点的 y 坐 标、终点的 x 坐标、终点的 y 坐标。调用这个方法后,它就会创建一个指定大小的渐变,并返回 CanvasGradient 对象的实例。
  2. addColorStop()方法来指定色标。这个方法接收两个参数: 色标位置和 CSS 颜色值。色标位置是一个 0(开始的颜色)到 1(结束的颜色)之间的数字。
var gradient = context.createLinearGradient(30, 30, 70, 70);
gradient.addColorStop(0, "white");
gradient.addColorStop(1, "black");

gradient 对象表示的是一个从画布上点(30,30)到点(70,70)的渐变。起点的色标是白色,终 点的色标是黑色。然后就可以把 fillStyle 或 strokeStyle 设置为这个对象,从而使用渐变来绘制 形状或描边

//绘制红色矩形
context.fillStyle = "#ff0000"; 
context.fillRect(10, 10, 50, 50);
//绘制渐变矩形
context.fillStyle = gradient;
context.fillRect(30, 30, 50, 50);

确保渐变 与形状对齐:

function createRectLinearGradient(context, x, y, width, height){
        return context.createLinearGradient(x, y, x+width, y+height);
}

var gradient = createRectLinearGradient(context, 30, 30, 50, 50); 
gradient.addColorStop(0, "white");
gradient.addColorStop(1, "black");
//绘制渐变矩形
context.fi llStyle = gradient; context.fillRect(30, 30, 50, 50);
  1. createRadialGradient()方法(径向渐变[放射渐变]),接收 6 个参 数,对应着两个圆的圆心和半径。前三个参数指定的是起点圆的原心(x 和 y)及半径,后三个参数指 定的是终点圆的原心(x 和 y)及半径。
var gradient = context.createRadialGradient(55, 55, 10, 55, 55, 30);
gradient.addColorStop(0, "white");
gradient.addColorStop(1, "black");
//绘制红色矩形
context.fillStyle = "#ff0000"; context.fillRect(10, 10, 50, 50);
//绘制渐变矩形
context.fillStyle = gradient; context.fillRect(30, 30, 50, 50);

(9) 模式

模式其实就是重复的图像,可以用来填充或描边图形。

要创建一个新模式,可以调用 createPattern()方法并传入两个参数:一个 HTML <img>元素和一个表示如何重复图像的字符串。 其中,第二个参数的值与 CSS 的 background-repeat 属性值相同,包括"repeat"、"repeat-x"、 "repeat-y"和"no-repeat"。

var image = document.images[0],
pattern = context.createPattern(image, "repeat");
//绘制矩形
context.fillStyle = pattern; context.fillRect(10, 10, 150, 150);

模式与渐变一样,都是从画布的原点(0,0)开始的。将填充样式(fillStyle)设置 为模式对象,只表示在某个特定的区域内显示重复的图像,而不是要从某个位置开始绘制重复的图像。

(10) 使用图像数据

  1. getImageData()取得原始图像数据。这个方法接收 4 个参数:要取得其数据的画面区域的 x 和 y 坐标以及该区域的像素宽度和高度。
var imageData = context.getImageData(10, 5, 50, 50);
  1. 这里返回的对象是 ImageData 的实例。每个 ImageData 对象都有三个属性:width、height 和data。
  2. 其中 data 属性是一个数组,保存着图像中每一个像素的数据。在 data 数组中,每一个像素用4 个元素来保存,分别表示红、绿、蓝和透明度值。因此,第一个像素的数据就保存在数组的第 0 到第3 个元素中。

通过修改图像数据,可以像下面这样创建一个简单的灰阶过滤器:

var drawing = document.getElementById("drawing");
//确定浏览器支持<canvas>元素 
if (drawing.getContext){
var context = drawing.getContext("2d"),
        image = document.images[0],
        imageData, data,
        i, len, average,
        red, green, blue, alpha;
//绘制原始图像 
context.drawImage(image, 0, 0);
//取得图像数据
imageData = context.getImageData(0, 0, image.width, image.height); data = imageData.data;
    for (i=0, len=data.length; i < len; i+=4){
        red = data[i];
        green = data[i+1];
        blue = data[i+2];
        alpha = data[i+3];
//求得 rgb 平均值
average = Math.floor((red + green + blue) / 3);
//设置颜色值,透明度不变
 data[i] = average;
 data[i+1] = average; 
data[i+2] = average;
}
//回写图像数据并显示结果
imageData.data = data; 
context.putImageData(imageData, 0, 0);
}

(11) 合成

  1. globalAlpha 是一个介于 0 和 1 之间的值(包括 0 和 1),用于指定所有绘制的透 明度。默认值为 0。
//绘制红色矩形
context.fillStyle = "#ff0000"; 
context.fillRect(10, 10, 50, 50);
//修改全局透明度
 context.globalAlpha = 0.5;
//绘制蓝色矩形
context.fillStyle = "rgba(0,0,255,1)";
context.fillRect(30, 30, 50, 50);
//重置全局透明度 
context.globalAlpha = 0;
  1. globalCompositionOperation表示后绘制的图形怎样与先绘制的图形结合。这个 属性的值是字符串,可能的值如下:
    (1) source-over(默认值):后绘制的图形位于先绘制的图形上方。
    (2) source-in:后绘制的图形与先绘制的图形重叠的部分可见,两者其他部分完全透明。
    (3) source-out:后绘制的图形与先绘制的图形不重叠的部分可见,先绘制的图形完全透明。
    (4) source-atop:后绘制的图形与先绘制的图形重叠的部分可见,先绘制图形不受影响。
    (5) destination-over:后绘制的图形位于先绘制的图形下方,只有之前透明像素下的部分才可见。
    (6) destination-in:后绘制的图形位于先绘制的图形下方,两者不重叠的部分完全透明。
    (7) destination-out:后绘制的图形擦除与先绘制的图形重叠的部分。
    (8) destination-atop:后绘制的图形位于先绘制的图形下方,在两者不重叠的地方,先绘制的图形会变透明。
    (9) lighter:后绘制的图形与先绘制的图形重叠部分的值相加,使该部分变亮。
    (10) copy:后绘制的图形完全替代与之重叠的先绘制图形。
    (11) xor:后绘制的图形与先绘制的图形重叠的部分执行“异或”操作。

3. WebGL

(1) 类型化数组

  1. 类型化数组的核心就是一个名为ArrayBuffer 的类型。每个 ArrayBuffer 对象表示的只是内存 中指定的字节数,但不会指定这些字节用于保存什么类型的数据。通过 ArrayBuffer 所能做的,就是 为了将来使用而分配一定数量的字节。
var buffer = new ArrayBuffer(20);
  1. 创建了 ArrayBuffer 对象后,能够通过该对象获得的信息只有它包含的字节数,方法是访问其byteLength 属性:
var bytes = buffer.byteLength;
*视图

使用 ArrayBuffer(数组缓冲器类型)的一种特别的方式就是用它来创建数组缓冲器视图。其中, 最常见的视图是 DataView,通过它可以选择 ArrayBuffer 中一小段字节。

(1) 在创建 DataView 实例的时候传入一个 ArrayBuffer、一个可选的字节偏移量(从该字节开始选择)和一个可选的要选 择的字节数。

//基于整个缓冲器创建一个新视图
var view = new DataView(buffer);
//创建一个开始于字节 9 的新视图
var view = new DataView(buffer, 9);
//创建一个从字节 9 开始到字节 18 的新视图
var view = new DataView(buffer, 9, 10);

(2) DataView 对象会把字节偏移量以及字节长度信息分别保存在 byteOffsetbyteLength 属性中。

alert(view.byteOffset);
alert(view.byteLength);

(3) DataView 支持的数据类型以及相应的读写方法:



var buffer = new ArrayBuffer(20),
    view = new DataView(buffer),
    value;
view.setUint16(0, 25);
view.setUint16(2, 50); //不能从字节 1 开始,因为 16 位整数要用 2B 
value = view.getUint16(0);
var buffer = new ArrayBuffer(20),
    view = new DataView(buffer),
    value;
view.setUint16(0, 25);
value = view.getInt8(0);
alert(value); //0
*类型化视图

类型化视图一般也被称为类型化数组,因为它们除了元素必须是某种特定的数据类型外,与常规的 数组无异。

类型化视图也分几种,而且它们都继承了 DataView。

(1) Int8Array:表示 8 位二补整数。
(2) Uint8Array:表示 8 位无符号整数。
(3) Int16Array:表示 16 位二补整数。
(4) Uint16Array:表示 16 位无符号整数。
(5) Int32Array:表示 32 位二补整数。
(6) Uint32Array:表示 32 位无符号整数。
(7) Float32Array:表示 32 位 IEEE 浮点值。
(8) Float64Array:表示 64 位 IEEE 浮点值。

  1. 由于这些视图都继承自 DataView,因而可以使用相同的构造函数参数来实例化。第一个参数是要 使用 ArrayBuffer 对象,第二个参数是作为起点的字节偏移量(默认为 0),第三个参数是要包含的字 节数。三个参数中只有第一个是必需的。
//创建一个新数组,使用整个缓冲器
var int8s = new Int8Array(buffer);
//只使用从字节 9 开始的缓冲器
var int16s = new Int16Array(buffer, 9);
//只使用从字节 9 到字节 18 的缓冲器
var uint16s = new Uint16Array(buffer, 9, 10);
  1. 每个视图构造函数都有一个名为 BYTES_PER_ELEMENT的属性,表示类型化数组的每个元素需要多 少字节。
//需要 10 个元素空间
var int8s = new Int8Array(buffer, 0, 10 * Int8Array.BYTES_PER_ELEMENT);
//需要 5 个元素空间
var uint16s = new Uint16Array(buffer, int8s.byteOffset + int8s.byteLength,  5 * Uint16Array.BYTES_PER_ELEMENT);
  1. 使用类型化视图时,可以通过方括号语法访问每一个数据成员,可以通过 length 属性确定数组中有多少元素。
for (var i=0, len=int8s.length; i < len; i++){
    console.log("Value at position " + i + " is " + int8s[i]);
}
  1. 类型化视图还有一个方法,即 subarray(),使用这个方法可以基于底层数组缓冲器的子集创建一 个新视图。这个方法接收两个参数:开始元素的索引和可选的结束元素的索引。返回的类型与调用该方 法的视图类型相同。
var uint16s = new Uint16Array(10),
        sub = uint16s.subarray(2, 5);

(2) WebGL上下文

  1. 在支持的浏览器中,WebGL 的名字叫"experimental-webgl",如果浏览器不支持 WebGL, 那么取得该上下文时会返回 null。
var drawing = document.getElementById("drawing");
//确定浏览器支持<canvas>元素 if (drawing.getContext){
    var gl = drawing.getContext("experimental-webgl");
    if (gl){
//使用 WebGL }
}
  1. 通过给 getContext()传递第二个参数,可以为 WebGL 上下文设置一些选项。这个参数本身是一 个对象,可以包含下列属性。

(1) alpha:值为 true,表示为上下文创建一个 Alpha 通道缓冲区;默认值为 true。
(2) depth:值为 true,表示可以使用 16 位深缓冲区;默认值为 true。
(3) stencil:值为 true,表示可以使用 8 位模板缓冲区;默认值为 false。
(4) antialias:值为 true,表示将使用默认机制执行抗锯齿操作;默认值为 true。
(5) premultipliedAlpha:值为 true,表示绘图缓冲区有预乘 Alpha 值;默认值为 true。
(6) preserveDrawingBuffer:值为 true,表示在绘图完成后保留绘图缓冲区;默认值为 false。建议确实有必要的情况下再开启这个值,因为可能影响性能。

var drawing = document.getElementById("drawing");
//确定浏览器支持<canvas>元素
 if (drawing.getContext){
        var gl = drawing.getContext("experimental-webgl", { alpha: false}); 
 if (gl){
        //使用 WebGL
  }
}
*常量

在 WebGL 中,保存在上下文对象中的这些常量都没有 GL_前缀。

*方法命名
  1. 方法名的后缀会包含参数个数(1 到 4) 和接收的数据类型(f 表示浮点数,i 表示整数)。例如,gl.uniform4f()意味着要接收 4 个浮点数,而 gl.uniform3i()则表示要接收 3 个整数。
  2. 也有很多方法接收数组参数而非一个个单独的参数。这样的方法其名字中会包含字母 v(即 vector, 矢量)。因此,gl.uniform3iv()可以接收一个包含 3 个值的整数数组。
*准备绘图

在实际操作 WebGL 上下文之前,一般都要使用某种实色清除<canvas>,为绘图做好准备。

  1. clearColor()方法来指定要使用的颜色值,该方法接收 4 个参数:红、绿、蓝和透明度。 每个参数必须是一个 0 到 1 之间的数值,表示每种分量在最终颜色中的强度。
  2. clear()方法传入的参数 gl.COLOR_BUFFER_BIT告诉 WebGL 使用之前定义的颜色来填充相应区 域。
gl.clearColor(0,0,0,1);   //black
gl.clear(gl.COLOR_BUFFER_BIT);
*视口与坐标

开始绘图之前,通常要先定义 WebGL 的视口(viewport)。默认情况下,视口可以使用整个<canvas> 区域。

1.viewport()方法改变视口大小,传入 4 个参数:(视口相对于<canvas>元素的) x 坐标、y 坐标、宽度和高度。

  1. 视口坐标的原点(0,0)在<canvas>元素的左下角,x
    轴和 y 轴的正方向分别是向右和向上。
  2. 视口内部的坐标系与定义视口的坐标系也不一样。在视口内部,坐标原点(0,0)是视口的中心 点,因此视口左下角坐标为(-1,-1),而右上角坐标为(1,1)。
*缓冲区

顶点信息保存在 JavaScript 的类型化数组中,使用之前必须转换到 WebGL 的缓冲区。

  1. gl.createBuffer()创建缓冲区。
  2. gl.bindBuffer()绑定到 WebGL 上下文。
  3. gl.bufferData()数据填充缓冲区。
  4. gl.bufferData()的最后一个参数用于指定使用缓冲区的方式,取值范围是如下几个常量。
    (1) gl.STATIC_DRAW:数据只加载一次,在多次绘图中使用。
    (2) gl.STREAM_DRAW:数据只加载一次,在几次绘图中使用。
    (3) gl.DYNAMIC_DRAW:数据动态改变,在多次绘图中使用。
  5. drawElements()输出缓冲区的内容,也可以传入 gl.ELEMENT_ARRAY_BUFFER。
  6. gl.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);
gl.deleteBuffer(buffer);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,165评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,720评论 1 298
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,849评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,245评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,596评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,747评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,977评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,708评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,448评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,657评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,141评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,493评论 3 258
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,153评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,890评论 0 198
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,799评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,685评论 2 272

推荐阅读更多精彩内容