马黑黑 发表于 2024-4-30 11:37

用canvas画布绘制立方体框架

本帖最后由 马黑黑 于 2024-4-30 11:46 编辑 <br /><br /><style>
        #canv { display: block;position: relative; margin: 20px auto; border: 1px solid gray; }
</style>

<canvas id="canv" width="400" height="400"></canvas>

<script>   
var ctx = canv.getContext('2d');

class Box {
        constructor(x,y,width,color) {
                this.x = x;
                this.y = y;
                this.width = width;
                this.color = color;
        };
        drawPanel(context) {
                //正面
                context.beginPath();
                context.moveTo(this.x, this.y);
                context.lineTo(this.x + this.width, this.y);
                context.lineTo(this.x + this.width, this.y + this.width);
                context.lineTo(this.x, this.y + this.width);
                context.lineTo(this.x, this.y);
                //顶面
                context.lineTo(this.x + this.width/2, this.y - this.width/2);
                context.lineTo(this.x + this.width*3/2, this.y - this.width/2);
                context.lineTo(this.x + this.width, this.y);
                //右侧
                context.moveTo(this.x + this.width, this.y + this.width);
                context.lineTo(this.x + this.width*3/2, this.y + this.width/2);
                context.lineTo(this.x + this.width*3/2, this.y - this.width/2);
                context.stroke();
                //后面
                context.setLineDash();
                context.moveTo(this.x + this.width/2, this.y - this.width/2);
                context.lineTo(this.x + this.width/2, this.y + this.width/2);
                context.lineTo(this.x + this.width*3/2, this.y + this.width/2);
                //底面和左侧
                context.moveTo(this.x + this.width/2, this.y + this.width/2);
                context.lineTo(this.x, this.y + this.width);
                context.stroke();
        };
};

var box = new Box(50.5, 150.5, 200);
box.drawPanel(ctx);
</script>

马黑黑 发表于 2024-4-30 11:39

本帖最后由 马黑黑 于 2024-4-30 11:47 编辑 <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; }
</style>

<h2>代码</h2>
<div class='mum'>
<cl-cd data-idx="1">&lt;<span class="tDarkRed">style</span>&gt;</cl-cd>
<cl-cd data-idx="2">&nbsp; &nbsp; #canv { <span class="tBlue">display:</span> block;<span class="tBlue">position:</span> relative; <span class="tBlue">margin:</span> 20px auto; <span class="tBlue">border:</span> 1px solid gray; }</cl-cd>
<cl-cd data-idx="3">&lt;<span class="tDarkRed">/style</span>&gt;</cl-cd>
<cl-cd data-idx="4">&nbsp;</cl-cd>
<cl-cd data-idx="5">&lt;<span class="tDarkRed">canvas</span> <span class="tRed">id</span>=<span class="tMagenta">"canv"</span> width=<span class="tMagenta">"400"</span> height=<span class="tMagenta">"400"</span>&gt;&lt;<span class="tDarkRed">/canvas</span>&gt;</cl-cd>
<cl-cd data-idx="6">&nbsp;</cl-cd>
<cl-cd data-idx="7">&lt;<span class="tDarkRed">script</span>&gt;    </cl-cd>
<cl-cd data-idx="8"><span class="tBlue">var</span> ctx = canv.getContext(<span class="tMagenta">'2d'</span>);</cl-cd>
<cl-cd data-idx="9">&nbsp;</cl-cd>
<cl-cd data-idx="10"><span class="tBlue">class </span>Box {</cl-cd>
<cl-cd data-idx="11">&nbsp; &nbsp; constructor(x,y,width,color) {</cl-cd>
<cl-cd data-idx="12">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tBlue">this</span>.x = x;</cl-cd>
<cl-cd data-idx="13">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tBlue">this</span>.y = y;</cl-cd>
<cl-cd data-idx="14">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tBlue">this</span>.width = width;</cl-cd>
<cl-cd data-idx="15">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tBlue">this</span>.color = color;</cl-cd>
<cl-cd data-idx="16">&nbsp; &nbsp; };</cl-cd>
<cl-cd data-idx="17">&nbsp; &nbsp; drawPanel(context) {</cl-cd>
<cl-cd data-idx="18">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tGreen">//正面</span></cl-cd>
<cl-cd data-idx="19">&nbsp; &nbsp; &nbsp; &nbsp; context.beginPath();</cl-cd>
<cl-cd data-idx="20">&nbsp; &nbsp; &nbsp; &nbsp; context.moveTo(<span class="tBlue">this</span>.x, <span class="tBlue">this</span>.y);</cl-cd>
<cl-cd data-idx="21">&nbsp; &nbsp; &nbsp; &nbsp; context.lineTo(<span class="tBlue">this</span>.x + <span class="tBlue">this</span>.width, <span class="tBlue">this</span>.y);</cl-cd>
<cl-cd data-idx="22">&nbsp; &nbsp; &nbsp; &nbsp; context.lineTo(<span class="tBlue">this</span>.x + <span class="tBlue">this</span>.width, <span class="tBlue">this</span>.y + <span class="tBlue">this</span>.width);</cl-cd>
<cl-cd data-idx="23">&nbsp; &nbsp; &nbsp; &nbsp; context.lineTo(<span class="tBlue">this</span>.x, <span class="tBlue">this</span>.y + <span class="tBlue">this</span>.width);</cl-cd>
<cl-cd data-idx="24">&nbsp; &nbsp; &nbsp; &nbsp; context.lineTo(<span class="tBlue">this</span>.x, <span class="tBlue">this</span>.y);</cl-cd>
<cl-cd data-idx="25">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tGreen">//顶面</span></cl-cd>
<cl-cd data-idx="26">&nbsp; &nbsp; &nbsp; &nbsp; context.lineTo(<span class="tBlue">this</span>.x + <span class="tBlue">this</span>.width/2, <span class="tBlue">this</span>.y - <span class="tBlue">this</span>.width/2);</cl-cd>
<cl-cd data-idx="27">&nbsp; &nbsp; &nbsp; &nbsp; context.lineTo(<span class="tBlue">this</span>.x + <span class="tBlue">this</span>.width*3/2, <span class="tBlue">this</span>.y - <span class="tBlue">this</span>.width/2);</cl-cd>
<cl-cd data-idx="28">&nbsp; &nbsp; &nbsp; &nbsp; context.lineTo(<span class="tBlue">this</span>.x + <span class="tBlue">this</span>.width, <span class="tBlue">this</span>.y);</cl-cd>
<cl-cd data-idx="29">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tGreen">//右侧</span></cl-cd>
<cl-cd data-idx="30">&nbsp; &nbsp; &nbsp; &nbsp; context.moveTo(<span class="tBlue">this</span>.x + <span class="tBlue">this</span>.width, <span class="tBlue">this</span>.y + <span class="tBlue">this</span>.width);</cl-cd>
<cl-cd data-idx="31">&nbsp; &nbsp; &nbsp; &nbsp; context.lineTo(<span class="tBlue">this</span>.x + <span class="tBlue">this</span>.width*3/2, <span class="tBlue">this</span>.y + <span class="tBlue">this</span>.width/2);</cl-cd>
<cl-cd data-idx="32">&nbsp; &nbsp; &nbsp; &nbsp; context.lineTo(<span class="tBlue">this</span>.x + <span class="tBlue">this</span>.width*3/2, <span class="tBlue">this</span>.y - <span class="tBlue">this</span>.width/2);</cl-cd>
<cl-cd data-idx="33">&nbsp; &nbsp; &nbsp; &nbsp; context.stroke();</cl-cd>
<cl-cd data-idx="34">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tGreen">//后面</span></cl-cd>
<cl-cd data-idx="35">&nbsp; &nbsp; &nbsp; &nbsp; context.setLineDash();</cl-cd>
<cl-cd data-idx="36">&nbsp; &nbsp; &nbsp; &nbsp; context.moveTo(<span class="tBlue">this</span>.x + <span class="tBlue">this</span>.width/2, <span class="tBlue">this</span>.y - <span class="tBlue">this</span>.width/2);</cl-cd>
<cl-cd data-idx="37">&nbsp; &nbsp; &nbsp; &nbsp; context.lineTo(<span class="tBlue">this</span>.x + <span class="tBlue">this</span>.width/2, <span class="tBlue">this</span>.y + <span class="tBlue">this</span>.width/2);</cl-cd>
<cl-cd data-idx="38">&nbsp; &nbsp; &nbsp; &nbsp; context.lineTo(<span class="tBlue">this</span>.x + <span class="tBlue">this</span>.width*3/2, <span class="tBlue">this</span>.y + <span class="tBlue">this</span>.width/2);</cl-cd>
<cl-cd data-idx="39">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tGreen">//底面和左侧</span></cl-cd>
<cl-cd data-idx="40">&nbsp; &nbsp; &nbsp; &nbsp; context.moveTo(<span class="tBlue">this</span>.x + <span class="tBlue">this</span>.width/2, <span class="tBlue">this</span>.y + <span class="tBlue">this</span>.width/2);</cl-cd>
<cl-cd data-idx="41">&nbsp; &nbsp; &nbsp; &nbsp; context.lineTo(<span class="tBlue">this</span>.x, <span class="tBlue">this</span>.y + <span class="tBlue">this</span>.width);</cl-cd>
<cl-cd data-idx="42">&nbsp; &nbsp; &nbsp; &nbsp; context.stroke();</cl-cd>
<cl-cd data-idx="43">&nbsp; &nbsp; };</cl-cd>
<cl-cd data-idx="44">};</cl-cd>
<cl-cd data-idx="45">&nbsp;</cl-cd>
<cl-cd data-idx="46"><span class="tBlue">var</span> box = <span class="tBlue">new</span> Box(50.5, 150.5, 200);</cl-cd>
<cl-cd data-idx="47">box.drawPanel(ctx);</cl-cd>
<cl-cd data-idx="48">&lt;<span class="tDarkRed">/script</span>&gt;</cl-cd>
</div>

马黑黑 发表于 2024-4-30 11:46

这应该是较笨的画法吧,不过又不是画不出来。它实际上和我们在纸上画的一样的,区别仅在于,纸上画我们用笔,canvas上画用JS代码组织指令。

绘制指令就两种:moveTo(x,y) 和 lineTo(x,y),就是画笔移动到哪里、画条直线到哪里。画时,能连笔的尽量连笔,不能的就移笔后再画线,尽量在重叠直线上不重复绘制(但如果着色,还是得老老实实一面一面地画)。

马黑黑 发表于 2024-4-30 11:56

第 46 行代码:

    var box = new Box(50.5, 150.5, 200);

创建一个 Box 实例,参数为 x, y, width, color,其中,x, y 是立方体框架起笔坐标,width 是立方体任意面的宽高尺寸,color参数这里没用上(默认使用黑色)。

立方体的正面从 (50.5, 150.5) 起笔,使用0.5是为了让 stroke 出来的线条是平滑的细线条,这是画布画线的一个小技巧。立方体面的宽高为 200。这样,我们画出来的立方体就处在画布的正中央。

第 47 行代码:

    box.drawPanel(ctx);

这是调用了 Box 类(class)的方法 drawPanel(画笔) ,有实例化的 box 去完成。

马黑黑 发表于 2024-4-30 12:05

这里面,存在立方体八个顶点坐标的算法。本例采用了简化的方式,避免了复杂的三角函数运算:

先以左后的上顶点为例,x 坐标是 起笔x坐标 + width/2,y 坐标是 起笔y坐标 - width/2;再以右后的顶点为例,x 坐标是 起笔x坐标 + width*3/2,y 坐标是 起笔y坐标 - width/2;其它各个顶点依据类似规律计算。

南无月 发表于 2024-4-30 12:53

还是数学课。。烧脑一上午了,这会先溜了溜了。。{:4_170:}
https://642303.freep.cn/642303/za/33.png

马黑黑 发表于 2024-4-30 13:04

南无月 发表于 2024-4-30 12:53
还是数学课。。烧脑一上午了,这会先溜了溜了。。

你可以那尺子来量一量就不烧脑了{:4_170:}

南无月 发表于 2024-4-30 18:02

马黑黑 发表于 2024-4-30 13:04
你可以那尺子来量一量就不烧脑了
现在怀念代码画平面的。。。圆啊椭圆,长方形啥的。。
立体的要算好多数据啊。。。
老师这样的最强大脑才能处理的事情。。{:4_170:}

马黑黑 发表于 2024-4-30 18:41

南无月 发表于 2024-4-30 18:02
现在怀念代码画平面的。。。圆啊椭圆,长方形啥的。。
立体的要算好多数据啊。。。
老师这样的最强大脑 ...

一个立方体要六个面,相当于画平面矩形的六倍,其实还多,因为有衔接等问题要处理。

红影 发表于 2024-4-30 20:43

这个是一笔一笔硬画出来的呢,好在很多点的x或y的值是一样的。{:4_204:}

红影 发表于 2024-4-30 20:43

this.width*3/2挺巧妙,还以为要加一个再加1/2呢{:4_173:}

马黑黑 发表于 2024-4-30 20:47

红影 发表于 2024-4-30 20:43
this.width*3/2挺巧妙,还以为要加一个再加1/2呢

这是简化算式,其实也是从你说的思路来的

马黑黑 发表于 2024-4-30 20:47

红影 发表于 2024-4-30 20:43
这个是一笔一笔硬画出来的呢,好在很多点的x或y的值是一样的。

几何好的,这么画应该不在话下吧

南无月 发表于 2024-4-30 20:48

马黑黑 发表于 2024-4-30 18:41
一个立方体要六个面,相当于画平面矩形的六倍,其实还多,因为有衔接等问题要处理。

看着又复杂又精巧。。
不过也习惯了看你进行这样精密的计算。。{:4_199:}

红影 发表于 2024-4-30 20:50

马黑黑 发表于 2024-4-30 11:56
第 46 行代码:

    var box = new Box(50.5, 150.5, 200);


“使用0.5是为了让 stroke 出来的线条是平滑的细线条,这是画布画线的一个小技巧。”
这个没想到,本来按照计算的话,应该就是整数的50和150呢。

马黑黑 发表于 2024-4-30 20:51

南无月 发表于 2024-4-30 20:48
看着又复杂又精巧。。
不过也习惯了看你进行这样精密的计算。。

第二种画法有空也去瞅瞅

马黑黑 发表于 2024-4-30 20:52

红影 发表于 2024-4-30 20:50
“使用0.5是为了让 stroke 出来的线条是平滑的细线条,这是画布画线的一个小技巧。”
这个没想到,本来 ...

对,如果不需要细线条的话

红影 发表于 2024-4-30 20:55

马黑黑 发表于 2024-4-30 11:56
第 46 行代码:

    var box = new Box(50.5, 150.5, 200);


color参数这里没用上(默认使用黑色)

那么颜色怎么加呢?我试着随便加个颜色,什么都看不到了{:4_173:}

马黑黑 发表于 2024-4-30 21:00

红影 发表于 2024-4-30 20:55
color参数这里没用上(默认使用黑色)

那么颜色怎么加呢?我试着随便加个颜色,什么都看不到了{:4_173 ...

加颜色要加对。box 是 Box 的实例化,那么,box.color = 'red' 就是定义了颜色了。

红影 发表于 2024-4-30 23:44

马黑黑 发表于 2024-4-30 20:47
这是简化算式,其实也是从你说的思路来的

是啊,这样写很简洁呢。
页: [1] 2 3 4 5 6 7
查看完整版本: 用canvas画布绘制立方体框架