在 JavaScript 中新的绘图思路[每日前端夜话0xB8]

每日前端夜话0xB8

每日前端夜话,陪你聊前端。

每天晚上18:00准时推送。

正文共:1640 字

预计阅读时间:7 分钟

作者:slicker.me

翻译:疯狂的技术宅

来源:slicker.me

我曾经用 Python 海龟图形生成过这个图像,并有用 JavaScript 复制它的强烈冲动。

对于那些不熟悉海龟图形的人来说,这是一个使用虚拟“海龟”绘制图形的概念,当海龟四处移动时,它的尾巴会在屏幕上留下痕迹。海龟有几个简单的命令:向前/向后移动 x 步,向左/向右转 x 度等。

所以例如这个序列:

  • 前进100步
  • 向左转90度
  • 前进100步
  • 向左转90度
  • 前进100步
  • 向左转90度
  • 前进100步

会画一个正方形。每次移动后,乌龟的位置和方向都会更新,下一步移动将相对于之前的位置。有点类似于Canvas Path(你可以有一系列的 lineTo),但 Path 只能使用笛卡尔坐标(x 和 y)而不是方向(左/右/前/后)。

如果海龟朝北开始,左转 90 度它将指向西。再左 90 度会指向南等。

可以在 Logo(自20世纪60年代)和 Python 中使用海龟图形,但不能在 JavaScript 中使用。

但真的是这样吗?我突然意识到 context.rotate 基本上模仿 “左转/右转”,而 context.translatemoveTo/drawTo 结合起来就像“前进/后退”一样。

这绝对不是一种优雅或可扩展的图形编程方式 —— 有点像用蚯蚓绑鞋子:它看起来很酷,但只适用于某些条件。这些只是我的奇怪代码集中的另一个小发明。如果你玩真正的海龟图形,我建议你使用提供这种功能的 JS 库、Python 或 Logo。或者至少先创建移动和旋转海龟的功能,以便使你的代码可读。

我的第一反应是创建一个具有自己的坐标和方向的海龟对象,然后使用 trig 函数计算移动,但是 rotate/translate 解决方案肯定更有趣,并允许我几乎逐行翻译 Python 程序:

首先,让我们看一下 rotate 和 translate 方法的工作原理。他们基本上改变了坐标系:

  • rotate 旋转一个角度
  • translate 通过向量移动它

It's easiest to see it in an example: 通过下面这个例子中最容易理解:

img

<html>
<style>
    body {  background-color: black;}
</style>
<body>
  <canvas id='myCanvas' width='800' height='600'></canvas>
  <script>
    function line(x1, y1, x2, y2) {
      context.beginPath();
      context.moveTo(x1, y1);
      context.lineTo(x2, y2);
      context.stroke();
    }

    function drawAxles() {
      line(-length, 0, length, 0);  // x axis
      line(length * .9, length * .1, length, 0);
      line(length * .9, -length * .1, length, 0);
      line(0, -length, 0, length);  // y axis
      line(-length * .1, length * .9, 0, length);
      line(length * .1, length * .9, 0, length);
    }

    let length = 100;
    let canvas = document.getElementById('myCanvas');
    let context = canvas.getContext('2d');

    context.strokeStyle = 'white';
    drawAxles();
    context.translate(length * 3, length);
    context.strokeStyle = 'blue';
    drawAxles();
    context.rotate(Math.PI / 8);
    context.strokeStyle = 'red';
    drawAxles();
    context.translate(3 * length, 0);
    context.strokeStyle = 'green';
    drawAxles();
  </script>
</body>

左上角的白色(半)箭头是 HTML5 Canvas 的标准初始 X(水平)和 Y 轴(垂直)。注意,Y 轴指向下方 —— 与你在学校学到的笛卡尔坐标系相反。

轴的负部分位于屏幕之外。

现在我们用 translate 来向右和向下移动坐标系 —— 也就是这些蓝色箭头。

接下来,我们将坐标系旋转几度并绘制红色箭头。请注意,原点(0, 0)仍然与蓝色原点位于同一位置。

最后,我们将系统在 x 轴上移动并将其绘制为绿色。请注意,上一步的轮换仍然适用。

现在让我们看看原始 Python 代码的 JavaScript 版本:

<html>
<style>
    body {  background-color: black;}
</style>
<canvas id='myCanvas' width='800' height='600'></canvas>
  <body>
    <script>
      let colors = ['red', 'purple', 'blue', 'green', 'orange', 'yellow'];
      let canvas = document.getElementById('myCanvas');
      let context = canvas.getContext('2d');
      context.scale(.3, .3);
      context.translate(canvas.width, canvas.height);
      for (let i = 0; i < 360; i++) {
        context.strokeStyle = colors[i % 6];
        context.lineWidth = i / 100 + 1;
        context.beginPath();
        context.moveTo(0, 0);
        context.lineTo(0, i);
        context.stroke();
        context.translate(0, i);
        context.rotate(-59 * (2 * Math.PI / 360));
      }
    </script>
  </body>
</canvas>

在第[11]行中,我缩小了图像。否则如果我保留原始的 Python 维度,它将会非常大。

[12] 将“海龟”移到画布的右下角。

[13-22] 绘制螺旋的主循环

[14]通过颜色数组([8])循环

[15]随着螺旋的增长改变线宽。它几乎不可见。

[16-20] 通过 i 步长向前移动海龟。[16-19] 画线,[20] 移动海龟。所以我们首先绘制线,并在事后更新“海龟”的位置。

当海龟离开中心时,线的长度变长。

[21] 将海龟旋转 59 度。负号只是为了保持螺旋方向。

现在让我们把螺旋旋转一下。只需几行代码就可以改变海龟转动的角度。我使用正弦函数[10]来实现,但如果你不是三角函数的粉丝,也可以使用不同的公式。甚至像 let rotation = counter / speed; 这样简单的东西产生有趣的结果(确保根据自己的喜好调整 [32] 中的速度)。

<html>
<style>
    body {  background-color: black;}
</style>
<body>
  <canvas id='myCanvas' width='800' height='600'></canvas>
    <script>

    function animate() {
      let rotation = (2 * Math.sin(counter / (3.14 * speed)));
      context.setTransform(scale, 0, 0, scale, canvas.width / 2, canvas.height / 2);
      context.clearRect(-canvas.width / 2, -canvas.height, canvas.width, canvas.height *2);
      for (let i = 0; i < 360; i++) {
        context.strokeStyle = colors[i % 6];
        context.lineWidth = i / 100 + 1;
        context.beginPath();
        context.moveTo(0, 0);
        context.lineTo(0, i);
        context.stroke();
        context.translate(0, i);
        context.rotate((-60 + rotation) * 2 * Math.PI / 360);
      }
      window.requestAnimationFrame(animate);
      counter++;
    }

    let colors = ['red', 'purple', 'blue', 'green', 'orange', 'yellow'];
    let canvas = document.getElementById('myCanvas');
    let context = canvas.getContext('2d');
    let counter = 0;
    let scale = .3;
    let speed = 20;
    animate();
    </script>
  </canvas>
</body>

原文:https://slicker.me/javascript/turtle.htm

本文分享自微信公众号 - 前端先锋(jingchengyideng)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-09-03

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏程序员成长充电站

比编程语言更重要的是什么?

在您真正了解语言之前,您必须学习许多不同的编程语言。出于本文的目的,我将把它们分成两个不同的类别:

8510
来自专栏Python爬虫与数据挖掘

可视化图解Python科学计算包NumPy

NumPy包是python生态系统中数据分析、机器学习和科学计算的主力。它极大地简化了向量和矩阵的操作。Python的一些主要软件包依赖于NumPy作为其基础架...

13130
来自专栏AI科技大本营的专栏

学会这21条,你离Vim大神就不远了

导语:作者本人是 Vim 的重度使用者,就因为喜欢上这种双手不离键盘就可以操控一切的feel,Vim 可以让人对文本的操作更加精准、高效。对于未使用过 Vim ...

13530
来自专栏程序员的知识天地

聊Python小白如何系统自学成为Python大牛(上)

很多人在自学Python的时候,总是不知道如何学习,不知道该怎么学,今天看到框架,就想学flask或者其他框架,但是当学的时候又茫然了,不知道怎么学;想学Pyt...

13920
来自专栏AI算法与图像处理

scikit-image图像处理入门

skimage是纯python语言实现的BSD许可开源图像处理算法库,主要的优势在于:

24630
来自专栏互联网技术杂谈

谈谈Python多线程

简而言之,因为CPython的内存管理不是线程安全的,所以需要加一个全局解释锁来保障Python内部对象是线程安全的。

44810
来自专栏萌海无涯

Python之pygame学习绘制图片(8)

11620
来自专栏量化投资与机器学习

一文掌握 __name__ 变量和在Python中的用法

在研究Python代码时,你可能经常会看到 __name__ 变量。下面是一个示例代码:

13850
来自专栏程序员成长充电站

从汇编语言到高级编程语言的演变

葛丽丝·霍普博士(作为美国海军军官,她是哈佛1号计算机的首批程序员之一)也遇到了这个问题,这台机器我们在前面提过。这台巨大机电野兽在 1944 年战时建造完成,...

14720
来自专栏萌海无涯

Python之pygame学习绘制文字制作滚动文字(6)

blit(source,dest,area = None,special_flags = 0) - > Rect

31120

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励