马黑黑 发表于 2024-5-19 12:09

canvas画布绘制钟摆

本帖最后由 马黑黑 于 2024-5-19 17:22 编辑 <br /><br /><style>
.mum { position: relative; margin: 0; padding: 10px; font: normal 16px/20px Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; color: black; background: rgba(240, 240, 240,.95); box-shadow: 2px 2px 4px gray; border: thick groove lightblue; border-radius: 6px; }
.mum ::selection { background-color: rgba(0,100,100,.35); }
.mum div { margin: 0; padding: 0; }
.mum cl-cd { display: block; position: relative; margin: 0 0 0 50px; padding: 0 0 0 10px; white-space: pre-wrap; overflow-wrap: break-word; border-left: 1px solid silver; }
.mum cl-cd::before { position: absolute; content: attr(data-idx); width: 50px; color: gray; text-align: right; transform: translate(-70px); }
.tRed { color: red; }
.tBlue { color: blue; }
.tGreen { color: green; }
.tDarkRed { color: darkred; }
.tMagenta { color: magenta; }
#pdcanv { display: block; margin: 20px auto; }
</style>
<h2>效果:</h2>
<canvas id="pdcanv" width="600" height="240"></canvas>
<h2>代码:</h2>
<div class='mum'>
<cl-cd data-idx="1">&lt;<span class="tDarkRed">canvas</span> <span class="tRed">id</span>=<span class="tMagenta">"pdcanv"</span> width=<span class="tMagenta">"600"</span> height=<span class="tMagenta">"240"</span>&gt;&lt;<span class="tDarkRed">/canvas</span>&gt;</cl-cd>
<cl-cd data-idx="2">&nbsp;</cl-cd>
<cl-cd data-idx="3">&lt;<span class="tDarkRed">script</span>&gt;</cl-cd>
<cl-cd data-idx="4">&nbsp;</cl-cd>
<cl-cd data-idx="5"><span class="tBlue">var</span> pdCtx = pdcanv.getContext(<span class="tMagenta">'2d'</span>); <span class="tGreen">//画笔</span></cl-cd>
<cl-cd data-idx="6">&nbsp;</cl-cd>
<cl-cd data-idx="7"><span class="tGreen">//创建钟摆的类</span></cl-cd>
<cl-cd data-idx="8"><span class="tBlue">class </span>Pendulum {</cl-cd>
<div class="tGreen"><cl-cd data-idx="9">&nbsp; &nbsp; /* 构造函数</cl-cd>
<cl-cd data-idx="10">&nbsp; &nbsp;    ctx&nbsp;: 画笔</cl-cd>
<cl-cd data-idx="11">&nbsp; &nbsp;    line&nbsp;: 配置对象,格式为 { <span class="tBlue">x:</span> x, <span class="tBlue">y:</span> y, <span class="tBlue">len:</span> len, <span class="tBlue">vx:</span> vx, <span class="tBlue">speed:</span> speed }</cl-cd>
<cl-cd data-idx="12">&nbsp; &nbsp; */</cl-cd></div>
<cl-cd data-idx="13">&nbsp; &nbsp; constructor(ctx,line) {</cl-cd>
<cl-cd data-idx="14">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tBlue">this</span>.ctx = ctx;</cl-cd>
<cl-cd data-idx="15">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tBlue">this</span>.line = line;</cl-cd>
<cl-cd data-idx="16">&nbsp; &nbsp; };</cl-cd>
<cl-cd data-idx="17">&nbsp;</cl-cd>
<cl-cd data-idx="18">&nbsp; &nbsp; <span class="tGreen">//draw方法 : 绘制静态钟摆</span></cl-cd>
<cl-cd data-idx="19">&nbsp; &nbsp; draw() {</cl-cd>
<div class="tGreen"><cl-cd data-idx="20">&nbsp; &nbsp; &nbsp; &nbsp; /* x1、y1是线段底端XY坐标也是摆锤圆心坐标</cl-cd>
<cl-cd data-idx="21">&nbsp; &nbsp; &nbsp; &nbsp;    x1 等于顶端 x + 摆幅(即vx)</cl-cd>
<cl-cd data-idx="22">&nbsp; &nbsp; &nbsp; &nbsp;    y1 等于,根据长度 len 和 x1 用勾股定理计算出来</cl-cd>
<cl-cd data-idx="23">&nbsp; &nbsp; &nbsp; &nbsp; */</cl-cd></div>
<cl-cd data-idx="24">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tBlue">var</span> x1 = <span class="tBlue">this</span>.line.x + <span class="tBlue">this</span>.line.vx;</cl-cd>
<cl-cd data-idx="25">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tBlue">var</span> y1 = <span class="tRed">Math</span>.sqrt(<span class="tBlue">this</span>.line.len ** 2 - (x1 - <span class="tBlue">this</span>.line.x) ** 2);</cl-cd>
<cl-cd data-idx="26">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tGreen">//画线</span></cl-cd>
<cl-cd data-idx="27">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tBlue">this</span>.ctx.beginPath();</cl-cd>
<cl-cd data-idx="28">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tBlue">this</span>.ctx.fillStyle = <span class="tBlue">this</span>.ctx.strokeStyle = <span class="tMagenta">'plum'</span>;</cl-cd>
<cl-cd data-idx="29">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tBlue">this</span>.ctx.lineWidth = 2;</cl-cd>
<cl-cd data-idx="30">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tBlue">this</span>.ctx.moveTo(<span class="tBlue">this</span>.line.x, <span class="tBlue">this</span>.line.y);</cl-cd>
<cl-cd data-idx="31">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tBlue">this</span>.ctx.lineTo(x1, y1);</cl-cd>
<cl-cd data-idx="32">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tBlue">this</span>.ctx.stroke();</cl-cd>
<cl-cd data-idx="33">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tGreen">//画圆</span></cl-cd>
<cl-cd data-idx="34">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tBlue">this</span>.ctx.beginPath();</cl-cd>
<cl-cd data-idx="35">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tBlue">this</span>.ctx.arc(x1, y1, 20, 0, 2*<span class="tRed">Math</span>.PI);</cl-cd>
<cl-cd data-idx="36">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tBlue">this</span>.ctx.fill();</cl-cd>
<cl-cd data-idx="37">&nbsp; &nbsp; };</cl-cd>
<cl-cd data-idx="38">&nbsp; &nbsp; <span class="tGreen">//update 方法 : 改变摆幅 vx 并调用 draw 静态绘制方法</span></cl-cd>
<cl-cd data-idx="39">&nbsp; &nbsp; update() {</cl-cd>
<cl-cd data-idx="40">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tBlue">this</span>.draw();</cl-cd>
<cl-cd data-idx="41">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tBlue">this</span>.line.vx += <span class="tBlue">this</span>.line.speed; <span class="tGreen">//改变摆幅</span></cl-cd>
<cl-cd data-idx="42">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tGreen">//水平方向摆动幅度不超过 ±30</span></cl-cd>
<cl-cd data-idx="43">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tBlue">if</span> (<span class="tRed">Math</span>.abs(<span class="tBlue">this</span>.line.vx) &gt;= 30) <span class="tBlue">this</span>.line.speed = -<span class="tBlue">this</span>.line.speed;</cl-cd>
<cl-cd data-idx="44">&nbsp; &nbsp; };</cl-cd>
<cl-cd data-idx="45">};</cl-cd>
<cl-cd data-idx="46">&nbsp;</cl-cd>
<cl-cd data-idx="47"><span class="tGreen">//配置钟摆 : 顶端XY坐标、长度、摆幅、速度</span></cl-cd>
<cl-cd data-idx="48"><span class="tBlue">var</span> option = { <span class="tBlue">x:</span> 300, <span class="tBlue">y:</span> 10, <span class="tBlue">len:</span> 200, <span class="tBlue">vx:</span> 0, <span class="tBlue">speed:</span> 1 };</cl-cd>
<cl-cd data-idx="49"><span class="tGreen">//创建钟摆类实例(参数两个 : 画笔、配置)</span></cl-cd>
<cl-cd data-idx="50"><span class="tBlue">var</span> pd = <span class="tBlue">new</span> Pendulum(pdCtx, option);</cl-cd>
<cl-cd data-idx="51">&nbsp;</cl-cd>
<cl-cd data-idx="52"><span class="tGreen">//渲染函数</span></cl-cd>
<cl-cd data-idx="53"><span class="tBlue">var</span> render = () =&gt; {</cl-cd>
<cl-cd data-idx="54">&nbsp; &nbsp; pdCtx.clearRect(0, 0, pdcanv.width, pdcanv.height);</cl-cd>
<cl-cd data-idx="55">&nbsp; &nbsp; pd.update(); <span class="tGreen">//调用类的 update 方法</span></cl-cd>
<cl-cd data-idx="56">&nbsp; &nbsp; requestAnimationFrame(render);</cl-cd>
<cl-cd data-idx="57">};</cl-cd>
<cl-cd data-idx="58">&nbsp;</cl-cd>
<cl-cd data-idx="59">render(); <span class="tGreen">//启动渲染</span></cl-cd>
<cl-cd data-idx="60">&nbsp;</cl-cd>
<cl-cd data-idx="61">&lt;<span class="tDarkRed">/script</span>&gt;</cl-cd>
</div>

<script>
var pdCtx = pdcanv.getContext('2d');

class Pendulum {
        constructor(ctx,line) {
                this.ctx = ctx;
                this.line = line;
        };
        draw() {
                var x1 = this.line.x + this.line.vx;
                var y1 = Math.sqrt(this.line.len ** 2 - (x1 - this.line.x) ** 2);
                this.ctx.beginPath();
                this.ctx.fillStyle = this.ctx.strokeStyle = 'plum';
                this.ctx.lineWidth = 2;
                this.ctx.moveTo(this.line.x, this.line.y);
                this.ctx.lineTo(x1, y1);
                this.ctx.stroke();
                this.ctx.beginPath();
                this.ctx.arc(x1, y1, 20, 0, 2*Math.PI);
                this.ctx.fill();
        };
        update() {
                this.draw();
                this.line.vx += this.line.speed;
                if (Math.abs(this.line.vx) >= 30) this.line.speed = -this.line.speed;
        };
};

var option = { x: 300, y: 10, len: 200, vx: 0, speed: 1 };
var pd = new Pendulum(pdCtx, option);

var render = () => {
        pdCtx.clearRect(0, 0, pdcanv.width, pdcanv.height);
        pd.update();
        requestAnimationFrame(render);
};

render();
</script>

红影 发表于 2024-5-19 14:36

这个讲解特别详细,canvas制作的小摆钟真漂亮{:4_199:}

红影 发表于 2024-5-19 14:38

这看不出y值的变化,如果摆幅大点,应该就能看出了{:4_173:}

马黑黑 发表于 2024-5-19 16:16

红影 发表于 2024-5-19 14:38
这看不出y值的变化,如果摆幅大点,应该就能看出了

理论上应该看得出来的吧?

摆动过程中,y1坐标最大值可以等于 len + y 的初始值即钟摆几长度+顶端Y坐标值,最小值可以等于 y 的初始值。此例,当 y1 = y 时,x1 = ±x

马黑黑 发表于 2024-5-19 16:17

红影 发表于 2024-5-19 14:36
这个讲解特别详细,canvas制作的小摆钟真漂亮

也不知道有木有错误,反正又不是不能摆{:4_170:}

红影 发表于 2024-5-19 17:21

马黑黑 发表于 2024-5-19 16:16
理论上应该看得出来的吧?

摆动过程中,y1坐标最大值可以等于 len + y 的初始值即钟摆几长度+顶端Y坐 ...

是的,理论上是可以看出来的{:4_187:}

红影 发表于 2024-5-19 17:21

马黑黑 发表于 2024-5-19 16:17
也不知道有木有错误,反正又不是不能摆

是啊,能正常摆起来就好{:4_173:}

马黑黑 发表于 2024-5-19 17:21

红影 发表于 2024-5-19 17:21
是的,理论上是可以看出来的
对的。先抽象,再具化。

马黑黑 发表于 2024-5-19 17:23

红影 发表于 2024-5-19 17:21
是啊,能正常摆起来就好

没错

红影 发表于 2024-5-19 18:40

马黑黑 发表于 2024-5-19 17:21
对的。先抽象,再具化。

具化就是把它摆到水平么{:4_173:}

红影 发表于 2024-5-19 18:40

马黑黑 发表于 2024-5-19 17:23
没错

目前看起来没问题呢{:4_187:}

马黑黑 发表于 2024-5-19 19:30

红影 发表于 2024-5-19 18:40
目前看起来没问题呢

没问题就是问题大了{:4_170:}

马黑黑 发表于 2024-5-19 19:34

红影 发表于 2024-5-19 18:40
具化就是把它摆到水平么

好比你做设计,如果你是优秀的设计师,你首先是从抽象开始你的工作:构思、算法的准备等等,无一不是先在大脑中有一个相对抽象的、不确切的思路,然后,逐一落实到图纸、计划书上,最后,具象化为产品。

红影 发表于 2024-5-19 21:20

马黑黑 发表于 2024-5-19 19:30
没问题就是问题大了

这叫什么逻辑{:4_173:}

红影 发表于 2024-5-19 21:23

马黑黑 发表于 2024-5-19 19:34
好比你做设计,如果你是优秀的设计师,你首先是从抽象开始你的工作:构思、算法的准备等等,无一不是先在 ...

黑黑就是这样实现设计的吧,这个结果很棒{:4_187:}

南无月 发表于 2024-5-19 22:43

看到这个教程想到了三体里的大钟摆。。。{:4_173:}

马黑黑 发表于 2024-5-19 22:54

南无月 发表于 2024-5-19 22:43
看到这个教程想到了三体里的大钟摆。。。

{:4_203:}

马黑黑 发表于 2024-5-19 22:55

红影 发表于 2024-5-19 21:23
黑黑就是这样实现设计的吧,这个结果很棒

这个是常规过程吧,当然俺不会写计划书{:4_170:}

马黑黑 发表于 2024-5-19 22:55

红影 发表于 2024-5-19 21:20
这叫什么逻辑

正确的逻辑

红影 发表于 2024-5-19 23:04

马黑黑 发表于 2024-5-19 22:55
这个是常规过程吧,当然俺不会写计划书

那就不能算设计师的事,应该大都事件的策划都如此呢。
页: [1] 2 3 4
查看完整版本: canvas画布绘制钟摆