马黑黑 发表于 2022-8-8 21:43

canvas画布如何融入帖子

本帖最后由 马黑黑 于 2022-8-8 23:04 编辑

本帖不仅仅讨论标题提出的命题,因为这个命题太简单了。怎么个简单法?重绘背景之时,将 fillStyle 设置为透明色,比如针对画笔 ctx :

    ctx.fillStyle = 'transparent';

或者:

    ctx.fillStyle = 'rgba(0,0,0,0)';

然后重绘背景(参数 width 和 height 是事先获得赋值的变量):

    ctx.fillRect(0,0,width,height);

如此,画布的背景色就是全透明的,而画布上绘制的元素是可见的,帖子之上,就好像没有画布一样。

还有一种方法,假如画布和帖子一样大小,则帖子父框不需要设置背景图片,直接用 drawImage 方法将已经预加载的图片标签(<img id="mypic" ... />)写入画布背景,取代上面的矩形填充(不需要设置 fillStyle):

            ctx.drawImage(mpic, 0, 0, width, height);

本帖标题提出的命题已完成。下面讲重点:通过对象实现canvas画布动画并令动画融入帖子。

首先需要准备CSS和HTML层面的东东。我们的预设是这样:画布只是帖子的一个组成部分,它以 400*400 的尺寸出现在帖子的正中央。CSS和HTML代码如下:

<style>
#papa { margin: auto; width: 1024px; height: 640px; box-shadow: 3px 3px 20px #000; position: relative; }
#mama { position: absolute; width: 400px; height: 400px; left: calc(50% - 200px); top: calc(50% - 200px); border: 1px solid pink; }
#canv { position: absolute; }
</style>

<div id="papa">
      <div id="mama">
                <canvas id="canv"></canvas>
      </div>
</div>


这是三层的DOM结构:父元素 papa、子元素 mama、孙元素 canv(不要纠结mama为什么是papa的儿子哈)。

下面是重点中的重点:JS作画。为了将来便于扩展,我们需要创建一个绘制对象,本例的绘制对象是球球,我们用函数构建方法创建一个球球对象,使用构建函数创建对象的方法惯例上要求对象名称头一个字母大写:

function Ball(x, y, r, vx, vy, color) {
      this.x = x; //圆心X坐标
      this.y = y; //圆心Y坐标
      this.r = r; //半径
      this.vx = vx; //横向偏移量
      this.vy = vy; //纵向偏移量
      this.color = color; //球体颜色
}


对象的好处在于,它可以集合我们所需要的无序化的属性,还有方法,将来需要的时候还可以随意增减。这里,Ball 对象我们定义了6个属性,这些属性在对象实例化时再进行赋值,例如,在我们创建一个新的 Ball 对象的时候:

      let myball = new Ball (60,60,20,0.5,1,'tan');

myball 通过 new Ball() 变成了一个 Ball 对象的实例,它的6个属性齐刷刷给了出来:X坐标60、Y坐标60、半径20、横向偏移量0.5、纵向偏移量1,颜色是棕褐色。myball实例化了Ball对象,它是可以直接被驱动的。当然,要驱动myball,有一些方法是Ball 对象通用的,就是说,Ball 对象应当创建有通用于所有实例化的类似于上面的 myball 的对象方法,我们可以使用 prototype 原型链属性来构建 Ball 的通用方法,例如,绘制球形:

Ball.prototype.draw = function() {
      ctx.beginPath(); //画笔路径启动
      ctx.fillStyle = this.color; //画笔填充色
      ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI); //绘制圆形
      ctx.fill(); //上色
}


这个实际上是一个名叫 draw 的函数,不过它专属于 Ball 对象的一个通用函数(或方法),提供给 Ball 的实例化对象使用:

      myball.draw();

这就画出了一个球形,球形的样式是按照 myball 提供的参数绘制的。

要使得球球运动,我们需要在画布上不停地擦除、重绘,球球的运动在画布上的体现是,擦除后在新的不远处重绘球球,就是说,arc() 画圆方法要改变圆心xy坐标进行重绘,达到圆从这个位置挪到了那个位置的效果。为此,我们需要为 Ball 对象创建一个改变 xy坐标的通用函数:

Ball.prototype.move = function() {
      if (this.x + this.r >= width || this.x - this.r <= 0) this.vx = -this.vx;
      if (this.y + this.r >= height || this.y - this.r <= 0) this.vy = -this.vy;
      this.x += this.vx;
      this.y += this.vy;
}


两个 if 语句分别处理横纵两个方向的变化,我们以横向变化为例加以说明:如果圆的X坐标+圆半径大于等于画布宽度,或者(||表示或者),如果圆的X坐标-半径小于等于0,则令圆的横向偏移量变为自己的负数。

为什么要这么做?看看后两句,圆的xy坐标依次加上 vx 或 vy 偏移量,加加加的结果是圆的位置会突破画布尺寸,圆心坐标加半径大于等于画布宽度后如果不做干预球球就不可见,所以令偏移量变为其负数,它就会往回走,走走走又走到左边和上边,突破了左边线或上边线的标志是 圆心坐标减去半径小于等于0,我们就干预它,再令其偏移量等于自己的负数,负负得正原理知道吧?如此反复,圆永远画地为牢,好可怜的圆圆。

那么,怎样才能让上面 Ball 对象不停地运行起来?嗯,我们还得为实例化的 Ball 对象 myball 建立一个永动机制:

//动画渲染函数
function render() {
      ctx.clearRect(0,0,width,height); //清除画布
      ctx.fillStyle = 'transparent'; //'rgba(0,0,0,0)'; //用透明色绘制画布底色
      ctx.fillRect(0,0,width,height); //填充矩形(draw() 函数就在这个矩形区域上作画)
      myball.draw(); //调用 draw 函数作画
      myball.move(); // 变更数据
      requestAnimationFrame(render); //递归调用 本函数:永动机制
}


渲染动画函数要手动运行一次,然后就可以喝杯咖啡观赏球球的运动了:

render();

如果需要画多个球,思路是创建多个 Ball 对象实例,用数组来加载这些实例。这是后话,现在的重点问题是要先理解和掌握如何操纵一个球。

本帖所使用的完整代码放在二楼。

马黑黑 发表于 2022-8-8 21:43

<style>
#papa { margin: auto; width: 1024px; height: 640px; box-shadow: 3px 3px 20px #000; position: relative; }
#mama { position: absolute; width: 400px; height: 400px; left: calc(50% - 200px); top: calc(50% - 200px); border: 1px solid pink; }
#canv { position: absolute; }
</style>

<div id="papa">
        <div id="mama"><canvas id="canv"></canvas></div>
</div>

<script>

let width = canv.width = mama.offsetWidth, height = canv.height = mama.offsetHeight;
let ctx = canv.getContext('2d');

//绘制对象
function Ball(x, y, r, vx, vy, color) {
        this.x = x;
        this.y = y;
        this.r = r;
        this.vx = vx;
        this.vy = vy;
        this.color = color;
}

Ball.prototype.draw = function() {
        ctx.beginPath();
        ctx.fillStyle = this.color
        ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI);
        ctx.fill();
}

Ball.prototype.move = function() {
        if (this.x + this.r >= width || this.x - this.r <= 0) this.vx = -this.vx;
        if (this.y + this.r >= height || this.y - this.r <= 0) this.vy = -this.vy;
        this.x += this.vx;
        this.y += this.vy;
}

let myball = new Ball (60,60,20,0.5,1,'tan');

function render() {
        ctx.clearRect(0,0,width,height);
        ctx.fillStyle = 'transparent'; //'rgba(0,0,0,0)';
        ctx.fillRect(0,0,width,height);
        myball.draw();
        myball.move();
        requestAnimationFrame(render);
}

render();

</script>

红影 发表于 2022-8-8 23:15

这个讲解真详细,黑黑辛苦了{:4_187:}

马黑黑 发表于 2022-8-9 07:39

红影 发表于 2022-8-8 23:15
这个讲解真详细,黑黑辛苦了

看看能不能理解、消化

加林森 发表于 2022-8-9 10:21

来学习。

马黑黑 发表于 2022-8-9 12:49

加林森 发表于 2022-8-9 10:21
来学习。

这个能学会就是高手了(不指套用,真正学会)

加林森 发表于 2022-8-9 13:14

马黑黑 发表于 2022-8-9 12:49
这个能学会就是高手了(不指套用,真正学会)

嗯嗯。我学习得很认真的。

红影 发表于 2022-8-9 19:56

马黑黑 发表于 2022-8-9 07:39
看看能不能理解、消化

无法理解,我不知道#mama的作用是什么,它的大小比底版小,球球也比它小,它的存在是做什么用的?

红影 发表于 2022-8-9 19:59

把黑黑给的代码在本地运行了一下,现在知道了,#mama是给出画布,好吧,我收回上面的问题{:4_173:}
没仔细看,一直以为这个是无数小球的那个帖子的解释呢。
所以没想通为什么#mama的尺寸只有400,呵呵,现在知道了。

红影 发表于 2022-8-9 20:07

这个演示是一个球,在#mama规定的范围内无限运动。
球的半径20,初始位置是60 60 ,偏移量是0.5和1

红影 发表于 2022-8-9 20:10

具体例子看懂了,通用方法里的这句
ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI); //绘制圆形
没看懂。xyr没问题,后面的两个是什么?0到360?和(x, y, r, vx, vy, color) 是什么关系?

马黑黑 发表于 2022-8-9 20:26

红影 发表于 2022-8-9 20:10
具体例子看懂了,通用方法里的这句
ctx.arc(this.x, this.y, this.r, 0, 2 * Math.PI); //绘制圆形
没看 ...

这是画圆的参数,0懂不懂没关系,就这么规定,2*Math.PI是画闭合的圆所必须的

马黑黑 发表于 2022-8-9 20:26

红影 发表于 2022-8-9 19:56
无法理解,我不知道#mama的作用是什么,它的大小比底版小,球球也比它小,它的存在是做什么用的?

mama是放画布的地方

马黑黑 发表于 2022-8-9 20:29

红影 发表于 2022-8-9 19:59
把黑黑给的代码在本地运行了一下,现在知道了,#mama是给出画布,好吧,我收回上面的问题
没仔细 ...

融入帖子,画布只是帖子的一个组成部分。这是讨论前规定好的

红影 发表于 2022-8-10 16:46

马黑黑 发表于 2022-8-9 20:26
这是画圆的参数,0懂不懂没关系,就这么规定,2*Math.PI是画闭合的圆所必须的

恩,正好是一周。

红影 发表于 2022-8-10 16:47

马黑黑 发表于 2022-8-9 20:26
mama是放画布的地方

我弄错了,以为是满布的,所以对尺寸很疑问{:4_173:}

红影 发表于 2022-8-10 16:48

马黑黑 发表于 2022-8-9 20:29
融入帖子,画布只是帖子的一个组成部分。这是讨论前规定好的

嗯嗯,知道了,谢谢黑黑{:4_187:}

马黑黑 发表于 2022-8-10 18:27

红影 发表于 2022-8-10 16:48
嗯嗯,知道了,谢谢黑黑

{:4_181:}

马黑黑 发表于 2022-8-10 18:28

红影 发表于 2022-8-10 16:47
我弄错了,以为是满布的,所以对尺寸很疑问

我应该设计一个border边框的

马黑黑 发表于 2022-8-10 18:29

红影 发表于 2022-8-10 16:46
恩,正好是一周。

其实你可以试一试把 0 改为别的,你就能感受的出来
页: [1] 2
查看完整版本: canvas画布如何融入帖子