用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: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"><<span class="tDarkRed">style</span>></cl-cd>
<cl-cd data-idx="2"> #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"><<span class="tDarkRed">/style</span>></cl-cd>
<cl-cd data-idx="4"> </cl-cd>
<cl-cd data-idx="5"><<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>><<span class="tDarkRed">/canvas</span>></cl-cd>
<cl-cd data-idx="6"> </cl-cd>
<cl-cd data-idx="7"><<span class="tDarkRed">script</span>> </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"> </cl-cd>
<cl-cd data-idx="10"><span class="tBlue">class </span>Box {</cl-cd>
<cl-cd data-idx="11"> constructor(x,y,width,color) {</cl-cd>
<cl-cd data-idx="12"> <span class="tBlue">this</span>.x = x;</cl-cd>
<cl-cd data-idx="13"> <span class="tBlue">this</span>.y = y;</cl-cd>
<cl-cd data-idx="14"> <span class="tBlue">this</span>.width = width;</cl-cd>
<cl-cd data-idx="15"> <span class="tBlue">this</span>.color = color;</cl-cd>
<cl-cd data-idx="16"> };</cl-cd>
<cl-cd data-idx="17"> drawPanel(context) {</cl-cd>
<cl-cd data-idx="18"> <span class="tGreen">//正面</span></cl-cd>
<cl-cd data-idx="19"> context.beginPath();</cl-cd>
<cl-cd data-idx="20"> context.moveTo(<span class="tBlue">this</span>.x, <span class="tBlue">this</span>.y);</cl-cd>
<cl-cd data-idx="21"> 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"> 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"> 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"> context.lineTo(<span class="tBlue">this</span>.x, <span class="tBlue">this</span>.y);</cl-cd>
<cl-cd data-idx="25"> <span class="tGreen">//顶面</span></cl-cd>
<cl-cd data-idx="26"> 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"> 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"> 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"> <span class="tGreen">//右侧</span></cl-cd>
<cl-cd data-idx="30"> 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"> 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"> 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"> context.stroke();</cl-cd>
<cl-cd data-idx="34"> <span class="tGreen">//后面</span></cl-cd>
<cl-cd data-idx="35"> context.setLineDash();</cl-cd>
<cl-cd data-idx="36"> 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"> 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"> 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"> <span class="tGreen">//底面和左侧</span></cl-cd>
<cl-cd data-idx="40"> 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"> 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"> context.stroke();</cl-cd>
<cl-cd data-idx="43"> };</cl-cd>
<cl-cd data-idx="44">};</cl-cd>
<cl-cd data-idx="45"> </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"><<span class="tDarkRed">/script</span>></cl-cd>
</div>
这应该是较笨的画法吧,不过又不是画不出来。它实际上和我们在纸上画的一样的,区别仅在于,纸上画我们用笔,canvas上画用JS代码组织指令。
绘制指令就两种:moveTo(x,y) 和 lineTo(x,y),就是画笔移动到哪里、画条直线到哪里。画时,能连笔的尽量连笔,不能的就移笔后再画线,尽量在重叠直线上不重复绘制(但如果着色,还是得老老实实一面一面地画)。 第 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 去完成。 这里面,存在立方体八个顶点坐标的算法。本例采用了简化的方式,避免了复杂的三角函数运算:
先以左后的上顶点为例,x 坐标是 起笔x坐标 + width/2,y 坐标是 起笔y坐标 - width/2;再以右后的顶点为例,x 坐标是 起笔x坐标 + width*3/2,y 坐标是 起笔y坐标 - width/2;其它各个顶点依据类似规律计算。 还是数学课。。烧脑一上午了,这会先溜了溜了。。{:4_170:}
https://642303.freep.cn/642303/za/33.png 南无月 发表于 2024-4-30 12:53
还是数学课。。烧脑一上午了,这会先溜了溜了。。
你可以那尺子来量一量就不烧脑了{:4_170:} 马黑黑 发表于 2024-4-30 13:04
你可以那尺子来量一量就不烧脑了
现在怀念代码画平面的。。。圆啊椭圆,长方形啥的。。
立体的要算好多数据啊。。。
老师这样的最强大脑才能处理的事情。。{:4_170:} 南无月 发表于 2024-4-30 18:02
现在怀念代码画平面的。。。圆啊椭圆,长方形啥的。。
立体的要算好多数据啊。。。
老师这样的最强大脑 ...
一个立方体要六个面,相当于画平面矩形的六倍,其实还多,因为有衔接等问题要处理。 这个是一笔一笔硬画出来的呢,好在很多点的x或y的值是一样的。{:4_204:} this.width*3/2挺巧妙,还以为要加一个再加1/2呢{:4_173:} 红影 发表于 2024-4-30 20:43
this.width*3/2挺巧妙,还以为要加一个再加1/2呢
这是简化算式,其实也是从你说的思路来的 红影 发表于 2024-4-30 20:43
这个是一笔一笔硬画出来的呢,好在很多点的x或y的值是一样的。
几何好的,这么画应该不在话下吧 马黑黑 发表于 2024-4-30 18:41
一个立方体要六个面,相当于画平面矩形的六倍,其实还多,因为有衔接等问题要处理。
看着又复杂又精巧。。
不过也习惯了看你进行这样精密的计算。。{:4_199:} 马黑黑 发表于 2024-4-30 11:56
第 46 行代码:
var box = new Box(50.5, 150.5, 200);
“使用0.5是为了让 stroke 出来的线条是平滑的细线条,这是画布画线的一个小技巧。”
这个没想到,本来按照计算的话,应该就是整数的50和150呢。 南无月 发表于 2024-4-30 20:48
看着又复杂又精巧。。
不过也习惯了看你进行这样精密的计算。。
第二种画法有空也去瞅瞅 红影 发表于 2024-4-30 20:50
“使用0.5是为了让 stroke 出来的线条是平滑的细线条,这是画布画线的一个小技巧。”
这个没想到,本来 ...
对,如果不需要细线条的话
马黑黑 发表于 2024-4-30 11:56
第 46 行代码:
var box = new Box(50.5, 150.5, 200);
color参数这里没用上(默认使用黑色)
那么颜色怎么加呢?我试着随便加个颜色,什么都看不到了{:4_173:} 红影 发表于 2024-4-30 20:55
color参数这里没用上(默认使用黑色)
那么颜色怎么加呢?我试着随便加个颜色,什么都看不到了{:4_173 ...
加颜色要加对。box 是 Box 的实例化,那么,box.color = 'red' 就是定义了颜色了。 马黑黑 发表于 2024-4-30 20:47
这是简化算式,其实也是从你说的思路来的
是啊,这样写很简洁呢。