马黑黑 发表于 2024-5-14 16:50

canvas画布:绘制椭圆坏绕文本

<style>
#mycanv { display: block; margin: 20px auto; }
.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>
<canvas id="mycanv" width="400" height="300"></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">"mycanv"</span> width=<span class="tMagenta">"400"</span> height=<span class="tMagenta">"300"</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> ww = mycanv.width, hh = mycanv.height;</cl-cd>
<cl-cd data-idx="6"><span class="tBlue">var</span> ctx = mycanv.getContext(<span class="tMagenta">'2d'</span>);</cl-cd>
<cl-cd data-idx="7">&nbsp;</cl-cd>
<cl-cd data-idx="8"><span class="tBlue">class </span>ccText {</cl-cd>
<cl-cd data-idx="9">&nbsp; &nbsp; constructor(text) {</cl-cd>
<cl-cd data-idx="10">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tBlue">this</span>.chars = text.split(<span class="tMagenta">''</span>);</cl-cd>
<cl-cd data-idx="11">&nbsp; &nbsp; };</cl-cd>
<cl-cd data-idx="12">&nbsp; &nbsp; draw(context) {</cl-cd>
<cl-cd data-idx="13">&nbsp; &nbsp; &nbsp; &nbsp; context.font = <span class="tMagenta">'bold 50px sans-serif'</span>;</cl-cd>
<cl-cd data-idx="14">&nbsp; &nbsp; &nbsp; &nbsp; context.textAlign = <span class="tMagenta">'center'</span>;</cl-cd>
<cl-cd data-idx="15">&nbsp; &nbsp; &nbsp; &nbsp; context.textBaseline = <span class="tMagenta">'middle'</span>;</cl-cd>
<cl-cd data-idx="16">&nbsp; &nbsp; &nbsp; &nbsp; context.shadowOffsetX = -2;</cl-cd>
<cl-cd data-idx="17">&nbsp; &nbsp; &nbsp; &nbsp; context.shadowOffsetY = 2;</cl-cd>
<cl-cd data-idx="18">&nbsp; &nbsp; &nbsp; &nbsp; context.shadowColor = <span class="tMagenta">'#000'</span>;</cl-cd>
<cl-cd data-idx="19">&nbsp; &nbsp; &nbsp; &nbsp; context.shadowBlur = 4;</cl-cd>
<cl-cd data-idx="20">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tBlue">var</span> a = 360 / <span class="tBlue">this</span>.chars.length, r1 = ww/2 - 60, r2 = hh/2 - 50;</cl-cd>
<cl-cd data-idx="21">&nbsp; &nbsp; &nbsp; &nbsp; <span class="tBlue">this</span>.chars.forEach((<span class="tBlue">char</span>,key) =&gt; {</cl-cd>
<cl-cd data-idx="22">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="tBlue">var</span> rad = (a * key - 90) * <span class="tRed">Math</span>.PI / 180;</cl-cd>
<cl-cd data-idx="23">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="tBlue">var</span> x = ww/2 + r1 * <span class="tRed">Math</span>.cos(rad), y = hh/2 + r2 * <span class="tRed">Math</span>.sin(rad);</cl-cd>
<cl-cd data-idx="24">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; context.fillStyle = `#${<span class="tRed">Math</span>.random().toString(16).substr(2,6)}`;</cl-cd>
<cl-cd data-idx="25">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; context.fillText(<span class="tBlue">char</span>, x, y);</cl-cd>
<cl-cd data-idx="26">&nbsp; &nbsp; &nbsp; &nbsp; });</cl-cd>
<cl-cd data-idx="27">&nbsp; &nbsp; };</cl-cd>
<cl-cd data-idx="28">};</cl-cd>
<cl-cd data-idx="29">&nbsp;</cl-cd>
<cl-cd data-idx="30"><span class="tBlue">var</span> cc = <span class="tBlue">new</span> ccText(<span class="tMagenta">'君不見黃河之水天上來'</span>);</cl-cd>
<cl-cd data-idx="31">cc.draw(ctx);</cl-cd>
<cl-cd data-idx="32">&nbsp;</cl-cd>
<cl-cd data-idx="33">&lt;<span class="tDarkRed">/script</span>&gt;</cl-cd>
</div>

<script>
var ww = mycanv.width, hh = mycanv.height;
var ctx = mycanv.getContext('2d');
class ccText {
        constructor(text) {
                this.chars = text.split('');
        };
        draw(context) {
                context.font = 'bold 50px sans-serif';
                context.textAlign = 'center';
                context.textBaseline = 'middle';
                context.shadowOffsetX = -2;
                context.shadowOffsetY = 2;
                context.shadowColor = '#000';
                context.shadowBlur = 4;
                var a = 360 / this.chars.length, r1 = ww/2 - 60, r2 = hh/2 - 50;
                this.chars.forEach((char,key) => {
                        var rad = (a * key - 90) * Math.PI / 180;
                        var x = ww/2 + r1 * Math.cos(rad), y = hh/2 + r2 * Math.sin(rad);
                        context.fillStyle = `#${Math.random().toString(16).substr(2,6)}`;
                        context.fillText(char, x, y);
                });
        };
};
var cc = new ccText('君不見黃河之水天上來');
cc.draw(ctx);
</script>

马黑黑 发表于 2024-5-14 17:00

思路:

利用三角函数 cos 和 sin 计算出每一个字绕椭圆圆周布排的点XY坐标值,大圆半径 r1 和小圆半径 r2 的依据分别是画布宽高各半,宽高不同就会得出椭圆绕排效果,宽高相同则得到圆形绕排效果。

【注】第20行代码,r1 宽除以2减60、r2 高除以2减50,减去的数基于两个层面进行考虑:一是接近文本大小的值(50px,见第13行代码),二是此例中宽度大于高度。也可以减去相同的数值。

马黑黑 发表于 2024-5-14 17:17

代码解释:

创建一个 ccText 类(代码8~28行),这个类通过构造函数(代码9~11行)将文本解析为数组,每一个数组元素包含一个字(字母)。

ccText 类有一个方法 draw(context)(代码12~27行),参数 context 指画笔。该方法:首先设置文本相关的属性(代码13~19行),有font设置、文本对齐设置、阴影设置等;接着,第20行代码,声明并计算平均角度 a、大圆半径 r1 和小圆半径 r2;然后,通过一个循环计算好每一个字的XY坐标点并将文字填充出来(代码在21~26行)。

关于圆周上XY坐标值的计算:

x = 圆心坐标x值 + 半径 * cos(弧度)
y = 圆心坐标y值 + 半径 * sin(弧度)

角度转弧度公式:

弧度 = 角度 * π / 180

第22行代码中,角度减去90是为了令第一个字位于最上方。原因:画布坐标系和HTML坐标系一样,0度指向3点钟方向,只有将其逆时针旋转90度,第一个字才会出现在圆周的顶点。

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

π 就是 PI,不要看成 n

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

画布的尺寸、文本的数量和字体的大小要配置得当,不然文字可能出现布局过于稀疏或彼此相互叠加的情况。

南无月 发表于 2024-5-14 18:10

大概看了一下,画布时钟教程是基础
可以用时钟数字排列理解字的排列~~{:4_173:}

马黑黑 发表于 2024-5-14 18:45

南无月 发表于 2024-5-14 18:10
大概看了一下,画布时钟教程是基础
可以用时钟数字排列理解字的排列~~

冰雪

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

马黑黑 发表于 2024-5-14 17:00
思路:

利用三角函数 cos 和 sin 计算出每一个字绕椭圆圆周布排的点XY坐标值,大圆半径 r1 和小圆半径 r ...

去试了一下,如果不减那个50,得到的文字只有一半了。其实减掉25就够用了吧{:4_173:}

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

这个还设置了阴影,还把第一个字细心地调到了最上面。很赞{:4_199:}

马黑黑 发表于 2024-5-14 19:49

红影 发表于 2024-5-14 19:28
这个还设置了阴影,还把第一个字细心地调到了最上面。很赞

谢赞

马黑黑 发表于 2024-5-14 19:52

红影 发表于 2024-5-14 19:24
去试了一下,如果不减那个50,得到的文字只有一半了。其实减掉25就够用了吧

你可以给画布一个 style 属性,领悟边界问题:

<canvas id="mycanv" width="400" height="300" style="border: 1px solid gray;"></canvas>

这是内联式CSS,用 style 属性来实现

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

马黑黑 发表于 2024-5-14 19:49
谢赞

不客气啊,整那么客气做什么{:4_173:}

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

马黑黑 发表于 2024-5-14 19:52
你可以给画布一个 style 属性,领悟边界问题:




嗯嗯,看到了{:4_187:}

马黑黑 发表于 2024-5-14 21:32

红影 发表于 2024-5-14 21:20
嗯嗯,看到了

这个,做什么都可以,用边框观察边界

马黑黑 发表于 2024-5-14 21:32

红影 发表于 2024-5-14 21:19
不客气啊,整那么客气做什么

不客气没有嘎嘎吃

红影 发表于 2024-5-14 21:50

马黑黑 发表于 2024-5-14 21:32
这个,做什么都可以,用边框观察边界

是的,下次吃不准的时候就可以加上这句了。

红影 发表于 2024-5-14 21:50

马黑黑 发表于 2024-5-14 21:32
不客气没有嘎嘎吃

那我选择不客气吧,我更喜欢吃素{:4_173:}

南无月 发表于 2024-5-14 21:51

马黑黑 发表于 2024-5-14 18:45
冰雪

{:4_170:} 这个教程还没看,动态的就出来了。大白老师好速度

马黑黑 发表于 2024-5-14 23:07

南无月 发表于 2024-5-14 21:51
这个教程还没看,动态的就出来了。大白老师好速度

哪里哪里

马黑黑 发表于 2024-5-14 23:07

红影 发表于 2024-5-14 21:50
那我选择不客气吧,我更喜欢吃素

老吃素会脑萎缩
页: [1] 2 3 4 5 6
查看完整版本: canvas画布:绘制椭圆坏绕文本