JavaScript高级程序设计(第三版) 第15章 Canvas

Menu

第15章 使用 Canvas 绘图

15.1 基本用法
15.2 2D 上下文
  • 15.2.1 填充和描边
  • 15.2.2 绘制矩形
  • 15.2.3 绘制路径
  • 15.2.4 绘制文本
  • 15.2.5 变换
  • 15.2.6 绘制图像
  • 15.2.7 阴影
  • 15.2.8 渐变
  • 15.2.9 模式
  • 15.2.10 使用图像数据
  • 15.2.11 合成

15.1 基本用法
  1. HTML里添加<canvas>元素:接着必须先设置其 width 和 height 属性,指定可以绘图的区域大小。出现在开始和结束标签中的内容是后备信息,如果浏览器不支持<canvas>元素,就会显示这些信息。下面就是<canvas>元素的例子。
<canvas id="drawing" width=" 200" height="200">A drawing of something.</canvas>
  1. 取得绘图上下文:要在这块画布(canvas)上绘图,需要取得绘图上下文。而取得绘图上下文对象的引用,需要调用getContext()方法并传入上下文的名字。传入"2d",就可以取得 2D 上下文对象。
var drawing = document.getElementById("drawing");
// 取得 2D 上下文对象
var context = drawing.getContext("2d");  
  1. 导出在<canvas>元素上绘制的图像:使用 toDataURL()方法,这个方法接受一个参数,即图像的 MIME

类型格式,而且适合用于创建图像的任何上下文。默认情况下,浏览器会将图像编码为 PNG 格式(除非另行指定

)。比如,要取得画布中的一幅 PNG 格式的图像。:

var drawing = document.getElementById("drawing");
//取得图像的数据 URI
var imgURI = drawing.toDataURL("image/png");
//显示图像
var image = document.createElement("img");
image.src = imgURI;
document.body.appendChild(image);

15.2 2D 上下文
  • 2D 上下文的坐标开始于<canvas>元素的左上角,原点坐标是(0,0)。

  • 15.2.1 填充和描边
  • 2D 上下文的两种基本绘图操作是填充和描边。填充,就是用指定的样式(颜色、渐变或图像)填
    充图形;描边,就是只在图形的边缘画线。操作的结果取决于两个属性: fillStyle 和 strokeStyle。
  • 这两个属性的值可以是字符串、渐变对象或模式对象,而且它们的默认值都是"#000000"。如果为它们指定表示颜色的字符串值,可以使用 CSS 中指定颜色值的任何格式,包括颜色名、十六进制码、rgb、 rgba、 hsl 或 hsla。举个例子:
var drawing = document.getElementById("drawing");
var context = drawing.getContext("2d");
// 所有涉及描边和填充的操作都将使用这两个样式,填充和描边的颜色需要写在位置前面;
context.strokeStyle = "red";
context.fillStyle = "#0000ff"

  • 15.2.2 绘制矩形

  • 矩形是唯一一种可以直接在 2D 上下文中绘制的形状。与矩形有关的方法包括 fillRect()、strokeRect()和 clearRect()。这三个方法都能接收 4 个参数:矩形的 x 坐标、矩形的 y 坐标、矩形宽度和矩形高度。这些参数的单位都是像素。

    • fillRect():方法在画布上绘制的矩形,默认填充黑色;
    • fillStyle:属性指定填充的颜色。可以使用 CSS 中指定颜色值的任何格式,包括颜色名、十六进制码、rgb、 rgba、 hsl 或 hsla。
//绘制红色矩形
context.fillStyle = "#ff0000";
// (x, y, width, height)
context.fillRect(10, 10, 50, 50);  
//绘制半透明的蓝色矩形
context.fillStyle = "rgba(0,0,255,0.5)";
context.fillRect(30, 30, 50, 50);
  • strokeStyle属性:描边颜色通过 strokeStyle 属性指定。
  • strokeRect()方法:方法在画布上绘制的矩形描边,默认填充黑色;
  • lineWidth属性:描边线条的宽度由 lineWidth 属性控制,该属性的值可以是任意整数;
  • lineCap 属性:可以控制线条末端的形状是平头、圆头还是方头("butt"、"round"或"square"),
  • lineJoin 属性:可以控制线条相交的方式是圆交、斜交还是斜接("round"、 "bevel"或"miter")。
//绘制红色描边矩形
context.strokeStyle = "#ff0000";
context.strokeRect(10, 10, 50, 50);
//绘制半透明的蓝色描边矩形
context.strokeStyle = "rgba(0,0,255,0.5)";
context.strokeRect(30, 30, 50, 50);
// 以上代码绘制了两个重叠的矩形。不过,这两个矩形都只有框线,内部并没有填充颜色
  • clearRect()方法:用于清除画布上的矩形区域。这个方法可以把绘制上下文中的某一矩形区域变透明。
ctx.fillRect(0, 0, 100, 100);
ctx.clearRect(30, 30, 40, 40)

以上code效果

  • 15.2.3 绘制路径
  • beginPath()方法:开始绘制新路径。
  • arc(x, y, radius, startAngle, endAngle, counterclockwise):绘制圆形;参数如下:
    • x:圆弧中心(圆心)的 x 轴坐标。
    • y:圆弧中心(圆心)的 y 轴坐标。
    • radius:圆弧的半径。
    • startAngle:圆弧的起始点, x轴方向开始计算,单位以弧度表示。
    • endAngle:圆弧的终点, 单位以弧度表示。(0, 2*Math.PI 表示画整个圆)
    • anticlockwise :可选的Boolean值 ,如果为 true,逆时针绘制圆弧,反之,顺时针绘制。
canvas = document.getElementById("drawing");
context = canvas.getContext("2d"); // 得到画图板对象; ?
context.beginPath();  // 创建新路径;
context.strokeStyle = "rgba(255,0,255,0.66)";
context.lineWidth = 3;
context.arc(250, 250, 100, 2*Math.PI, false);   //  弧度方法;
context.stroke();
  • lineTo(x, y):从上一点开始绘制一条直线,到(x,y)为止。
context.moveTo(250,250);
context.lineTo(250,160);
context.stroke();
  • moveTo(x, y):将绘图游标移动到(x,y),不画线。
  • arcTo(x1, y1, x2, y2, radius):从上一点开始绘制一条弧线,到(x2,y2)为止,并且以给定的半径 radius 穿过(x1,y1)。(是 Canvas 2D API 根据控制点和半径绘制圆弧路径,使用当前的描点(前一个moveTo或lineTo等函数的止点)。根据当前描点与给定的控制点1连接的直线,和控制点1与控制点2连接的直线,作为使用指定半径的圆的切线,画出两条切线之间的弧线路径。)
  • bezierCurveTo(c1x, c1y, c2x, c2y, x, y):贝塞尔曲线, 从上一点开始绘制一条曲线,到(x,y)为止,并且以(c1x,c1y)和(c2x,c2y)为控制点。
var c=document.getElementById("drawing");
var ctx=c.getContext("2d");
ctx.beginPath();
ctx.moveTo(50,250);
ctx.bezierCurveTo(200,150,300,350,450,250);
        
// cp1x     第一个贝塞尔控制点的 +


x 坐标
// cp1y     第一个贝塞尔控制点的 y 坐标
// cp2x     第二个贝塞尔控制点的 x 坐标
// cp2y     第二个贝塞尔控制点的 y 坐标
// x    结束点的 x 坐标
// y    结束点的 y 坐标
  • quadraticCurveTo(cx, cy, x, y):从上一点开始绘制一条二次曲线,到(x,y)为止,并且以(cx,cy)作为控制点。
canvas = document.getElementById("myCanvas");
context = canvas.getContext("2d");  // 创建canvas的执行环境;
context.beginPath();  // 创建画图路径;
context.moveTo(50,250);
context.quadraticCurveTo(250, 450,450, 250);  // 一个控制点的线;
context.stroke()   // 笔画描边;
  • rect(x, y, width, height):从点(x,y)开始绘制一个矩形,宽度和高度分别由 width 和height 指定。这个方法绘制的是矩形路径,而不是 strokeRect()和 fillRect()所绘制的独立的形状。
context.beginPath();  // 创建画图路径;
context.rect(50,50,100,65);
context.stroke()   // 笔画描边;
  • closePath():绘制一条连接到路径起点的线条;
context.beginPath();  // 创建画图路径;
context.moveTo(50,250);
context.quadraticCurveTo(250, 450,450, 250);  // 一个控制点的线;
context.closePath();
context.stroke();  // 笔画描边;
closePath()效果
  • fill():用 fillStyle 填充路径。
  • stroke():用strokeStyle描边路径。
  • clip() 方法从原始画布中剪切任意形状和尺寸。一旦剪切了某个区域,则所有之后的绘图都会被限制在被剪切的区域内(不能访问画布上的其他区域)。您也可以在使用 clip() 方法前通过使用 save() 方法对当前画布区域进行保存,并在以后的任意时间对其进行恢复(通过 restore() 方法)。
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
// 剪切矩形区域
ctx.rect(50,20,200,120);
ctx.stroke();
ctx.clip();
// 在 clip() 之后绘制绿色矩形
ctx.fillStyle="green";
ctx.fillRect(0,0,150,100);
不使用clip
使用clip

  • 15.2.4 绘制文本
  • 绘制文本主要有两个方法: fillText()strokeText()。这两个方法都可以接收 4 个参数:要绘制的文本字符串、 x 坐标、 y 坐标和可选的最大像素宽度。
    • 第四个参数表示字符的最大宽度限制:传入的字符串大于最大宽度,则绘制的文本字符的高度正确,但宽度会收缩以适应最大宽度。
  • font属性:表示文本样式、大小及字体,用 CSS 中指定字体的格式来指定,例如"10px Arial"。
  • textAlign属性:表示文本对齐方式。可能的有"start"、"end"、"left"、"right"和"center"。建议使用"start"和"end",不要使用"left"和"right",因为前两者的意思更稳妥,能同时适合从左到右和从右到左显示(阅读)的语言。
  • textBaseline属性:表示文本的基线。可能的值有"top"、"hanging"、"middle"、"alphabetic"、"ideographic"和"bottom"。
    • alphabetic:默认。文本基线是普通的字母基线。
    • top:文本基线是 em 方框的顶端。。
    • hanging:文本基线是悬挂基线。
    • middle:文本基线是 em 方框的正中。
    • ideographic:文本基线是表意基线。
    • bottom:文本基线是 em 方框的底端。
textBaseline

  • **Context.measureText() **
  • 方法返回一个 TextMetrics 对象,包含关于文本尺寸的信息(例如文本的宽度)。
var text = ctx.measureText("foo"); // TextMetrics object
text.width; // 16;
    var c = document.getElementById("myCanvas");
    var ctx = c.getContext("2d");
    fontsize = 150;
    ctx.font = fontsize + "px Arial";
    while(ctx.measureText("Hello world!").width > 500){

        fontsize-- ;
        ctx.font = fontsize + "px Arial";

    }
    ctx.textAlign = "center";
    ctx.textBaseline = "middle";
    ctx.translate(250, 250);
    ctx.rotate(90 * Math.PI / 180);
    ctx.scale(0.5, 0.5);
    ctx.fillText("Hello World", 10 , 10);

  • 15.2.5 变换
  • rotate(angle):围绕原点旋转图像 angle 弧度;要移动圆点, 使用下面的translate(x, y)方法;
    • 怎样实现原地旋转:
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.fillStyle = "pink";
ctx.fillRect(100,100,100,100)
var deg = Math.PI/180;  //deg = 1°
ctx.translate(150, 150);      //把原点点从零移到正方形的中心点
ctx.rotate(45*deg);    //绕着原点旋转45度(即绕着图像中心点旋转45度), 下面的transform也是同样作用
// transfrom要实现旋转,需要设置前4个参数
// ctx.transform(Math.cos(45*deg),Math.sin(45*deg),-Math.sin(45*deg),Math.cos(45*deg),0,0);
ctx.fillStyle = "blue";
ctx.fillRect(-50,-50,100,100) //旋转后的图像坐标需要从新的圆点回到原来的坐标, 即-width, -height

  • translate(x, y):将坐标原点移动到(x,y)。执行这个变换之后, 坐标(0,0)会变成之前由(x,y)表示的点。
ctx.font =  "150px Arial";
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.translate(250, 250);
ctx.rotate(90 * Math.PI / 180);
ctx.fillText("Hello World", 10 , 10);
  • scale(scaleX, scaleY):缩放图像,在 x 方向乘以 scaleX,在 y 方向乘以 scaleY。 scaleX和 scaleY 的默认值都是 1.0。
ctx.scale(0.5, 0.5);
ctx.fillText("Hello World", 10 , 10);
  • transform(a, b, c, d, e, f):直接修改变换矩阵。
    • 默认参数:transfrom(1, 0, 0, 1, 0, 0)
      a 水平缩放绘图
      b 水平倾斜绘图
      c 垂直倾斜绘图
      d 垂直缩放绘图
      e 水平移动绘图
      f 垂直移动绘图

  • 15.2.6 绘制图像

  • context.drawImage()

  • 要加入事件才能在画布里显示出图片

    • <img onload="somefunc(event)" src="image.jpg" id="myImg">
  • 如果你想把一幅图像绘制到画布上,可以使用 drawImage()方法。根据期望的最终结果不同,调用这个方法时,可以使用三种不同的参数组合。

    1. 传入一个 HTML <img>Element,以及绘制该图像的起点的 x 和 y 坐标.
    • context.drawImage(image, 10, 10);
    1. 如果你想改变绘制后图像的大小,可以再多传入两个参数,分别表示目标宽度和目标高度。
    • context.drawImage(image, 50, 10, 20, 30);
    1. 还可以选择把图像中的某个区域绘制到上下文中。 drawImage()方法的这种调
      用方式总共需要传入 9 个参数:
      要绘制的图像、
      源图像的 x 坐标、源图像的 y 坐标、
      源图像的宽度、源图像的高度、
      目标图像的 x 坐标、目标图像的 y 坐标、
      目标图像的宽度、目标图像的高度。
    • context.drawImage(图片obj, 源图片x,源图片y, x开始切的宽度,x开始切的高度,画布x点,画布y点,画布图片的宽度,画布图片的高度)
  • 除了给 drawImage()方法传入 HTML <img>元素外,还可以传入另一个<canvas>元素作为其第一个参数。这样,就可以把另一个画布内容绘制到当前画布上。

  • 获得图片修改后的结果:re = canvas.toDataURL(),可以把结果再放回到img元素里;img.src = re;

  • 即图像不能来自其他域。如果图像来自其他域,调用toDataURL()会抛出一个错误。

  • 15.2.7 阴影

  • 2D 上下文会根据以下几个属性的值,自动为形状或路径绘制出阴影。

context.shadowColor = "rgba(0,0,255,0.1)";  // 阴影颜色,不设置默认黑色和透明度 不透明是1
context.shadowOffsetX = -100;   // x 轴方向的阴影偏移量 默认0
context.shadowOffsetY = 1;   // y 轴方向的阴影偏移量 默认0
context.shadowBlur = 20;       // 模糊的像素数,默认 0,即不模糊
context.drawImage(img,50,50,200,150)

  • 15.2.8 渐变
    1. 可以调用 createLinearGradient()方法。这个方法接收 4 个参数:起点的 x 坐标、起点的 y 坐标、终点的 x 坐标、终点的 y 坐标。
    2. 下一步就是使用 addColorStop()方法来指定色标。这个方法接收两个参数:色标位置和 CSS 颜色值。色标位置是一个 0(开始的颜色)到 1(结束的颜色)之间的数字。
    3. 然后就可以把 fillStyle 或 strokeStyle 设置为这个对象,从而使用渐变来绘制形状或描边.
var gradient = context.createLinearGradient(30, 30, 80, 80);
gradient.addColorStop(0, "white");
gradient.addColorStop(1, "black");
//绘制渐变矩形
context.fillStyle = gradient;
context.fillRect(30, 30, 50, 50);
  1. 放射渐变
  • var gradient = context.createRadialGradient(55, 55, 10, 55, 55, 30);
  • 前三个参数指定的是起点圆的原心(x 和 y)及半径,后三个参数指定的是终点圆的原心(x 和 y)及半径。可以把径向渐变想象成一个长圆桶,而这 6 个参数定义的正是这个桶的两个圆形开口的位置。如果把一个圆形开口定义得比另一个小一些,那这个圆桶就变成了圆锥体,而通过移动每个圆形开口的位置,就可达到像旋转这个圆锥体一样的效果。
var gradient = context.createRadialGradient(55, 55, 10, 55, 55, 30);
gradient.addColorStop(0, "white");
gradient.addColorStop(1, "black");
//绘制渐变矩形
context.fillStyle = gradient;
context.fillRect(30, 30, 50, 50);

  • 15.2.9 模式
  • 模 式 其 实 就 是 重 复 的 图 像 , 可 以 用 来 填 充 或 描 边 图 形 。
  • 要 创 建 一 个 新 模 式 , 可 以 调 用createPattern()方法。
  • 有2个参数:1.imgElement 2.repeat方式: 包括"repeat"、 "repeat-x"、"repeat-y"和"no-repeat"。
img = document.getElementById("myImg");
canvasB = document.getElementById("myCanvasB");
contextB = canvasB.getContext("2d");
// 获取图像,把图像缩小到100x100像素;
contextB.drawImage(img, 0,0,100,100);

canvasA = document.getElementById("myCanvasA");
contextA = canvasA.getContext("2d");
// convasA的Context里创建Pattern,放缩小了的canvasB画布和repeat模式
pattern = contextA.createPattern(canvasB,"repeat");
// 填充规则
contextA.fillStyle = pattern;
contextA.fillRect(0,0,500,500)
}
  • createPattern()方法的第一个参数也可以是一个<video>元素,或者另一个<canvas>元素。

  • 15.2.10 使用图像数据
img = document.getElementById("myImg");
var c=document.getElementById("drawing");
var ctx=c.getContext("2d");
// img放到画布里;
ctx.drawImage(img,0,0,500,500);
// 取得图像数据
imgData = ctx.getImageData(0,0,c.width,c.height);
// 每个 ImageData 对象都有三个属性: width、 height 和data。
// 在imgData.data数组中,存着每一个像素点的rgba的值;
data = imgData.data;
for (let i=0, len=data.length; i<len; i+=4){
    //得到每个像素点的数据
    red = data[i];
    green = data[i+1];
    blue = data[i+2];
    //求得每个像素点rgb的平均值
    var average = Math.floor((red+green+blue)/3);
    //设置颜色值,透明度不变
    data[i] = average;
    data[i+1] = average;
    data[i+2] = average;
}
// 覆盖原来的imgData.data的值
imgData.data = data;
ctx.putImageData(imgData,0,0);

  • 15.2.11 合成
  • 上下文的属性:context.globalAlpha 设置透明度 1不透明 0透明 ;
canvas = document.getElementById("drawing");
context = canvas.getContext("2d");
context.globalAlpha = 0.5;    // 设置全局上下文的透明度
context.fillStyle = "red";
context.fillRect(0,0, 300, 150);  // 半透明红色
context.fillStyle = "blue";
context.fillRect(100,100, 300, 150);  // 半透明蓝色
  • globalCompositionOperation属性 表示后绘制的图形怎样与先绘制的图形结合。这个属性的值是字符串,可能的值如下。

    • source-over(默认值):后绘制的图形位于先绘制的图形上方。
    • source-in:后绘制的图形与先绘制的图形重叠的部分可见,两者其他部分完全透明。
    • source-out:后绘制的图形与先绘制的图形不重叠的部分可见,先绘制的图形完全透明。
    • source-atop:后绘制的图形与先绘制的图形重叠的部分可见,先绘制图形不受影响。
    • destination-over: 后绘制的图形位于先绘制的图形下方,只有之前透明像素下的部分才可见。
    • destination-in:后绘制的图形位于先绘制的图形下方,两者不重叠的部分完全透明。
    • destination-out:后绘制的图形擦除与先绘制的图形重叠的部分。
    • destination-atop:后绘制的图形位于先绘制的图形下方,在两者不重叠的地方,先绘制的图形会变透明。
    • lighter:后绘制的图形与先绘制的图形重叠部分的值相加,使该部分变亮。
    • copy:后绘制的图形完全替代与之重叠的先绘制图形。
    • xor:后绘制的图形与先绘制的图形重叠的部分执行“异或”操作。


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,192评论 4 369
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,186评论 1 303
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,844评论 0 252
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,471评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,876评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,891评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,068评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,791评论 0 205
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,539评论 1 249
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,772评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,250评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,577评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,244评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,146评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,949评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,995评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,812评论 2 276

推荐阅读更多精彩内容

  • 第15章 使用 Canvas 绘图 1. 基本用法 (1) 要使用 元素,必须先设置其width和 height ...
    yinxmm阅读 256评论 0 0
  • Core Graphics Framework是一套基于C的API框架,使用了Quartz作为绘图引擎。它提供了低...
    ShanJiJi阅读 1,459评论 0 20
  •   HTML5 添加的最受欢迎的功能就是 元素。这个元素负责在页面中设定一个区域,然后就可以通过 JavaScri...
    霜天晓阅读 2,946评论 0 2
  • 和我在成都的街头走一走 直到所有的灯都熄灭了也不停留
    Mangoqiu阅读 158评论 0 0
  • 上午在体育馆玩后到森林摩尔觅食,孩子说想吃披萨和蘑菇汤,这个地方很陌生,我就到处转着找(他们在停车场停车),转了两...
    土老冒乐园阅读 137评论 0 0