英文 | https://javascript.plainenglish.io/how-to-create-a-star-pattern-using-javascript-and-canvas-322f8af61ce1

翻译 | 杨小二

在本教程中,我们将使用 JavaScript 和 HTML5 Canvas API 创建下图横幅的星形图案。

因为即使创建一颗星也需要多行代码,所以我们将使用 drawJS——我构建的一个处理星形渲染的迷你库。

在此过程中,我们将使用对象字面量、嵌套的 for 循环和余数运算符来创建这种模式——以及一个 CSS 媒体查询来使横幅在窄屏幕宽度下调整大小。

使用 drawJS 的星形图案演示地址:https://codepen.io/nevkatz/pen/jOmexaK

drawStar 方法

图案是通过为图案中的每个星星调用 drawJS 库中的 drawStar 方法来创建的。假设我们只想创建图案中的第一颗星,如下所示。

在这种情况下,我们可以使用下面的代码调用一次 drawStar。

drawJS.drawStar({cx:50,cy:50,numPoints:5,stroke:'#622569',fill:'#b8a9c9',lineWidth:4,innerRadius: 20,outerRadius:35  });

现在,由于我们想要创建多个,因此,我们最终将编写一些更复杂的 JavaScript。但首先,让我们编写 HTML 和 CSS 并思考模式的特征。

打好基础

让我们从一些标记和样式开始。

HTML 和 CSS

HTML中的<canvas> 元素。

<canvas id="myCanvas" width="1300" height="600"></canvas>

我们的第一个 CSS 将使其成为块元素并将其居中。

canvas {display: block;margin: 10px auto;
}

为了使画布具有响应性,我们需要添加一个媒体查询,它将在屏幕宽度低于 1200 像素时将宽度设置为 100%。

我们还可以使用height: auto 来帮助我们的画布在调整大小时保持其纵横比。

@media screen and (max-width: 1200px) {canvas {width: 100%;height: auto;}
}

规划模式

在我们开始编写最初的 JavaScript 之前,让我们再次浏览一下星形模式并考虑制作它需要什么。

下面是模式的前两行。

要以这种方式排列星星,我们需要编写设置几个值的逻辑:

  • 将星星隔开

  • 连续的星星数

  • 行数

我们的程序还需要设置每颗星星的独特属性,包括以下内容:

  • 点数

  • 描边颜色

  • 填充颜色

星星还有一些共同的特性:

  • 边界宽度

  • 内半径,或恒星中心到每个角的距离

  • 外半径,或从恒星中心到每个外点的距离

最后,我们需要设置以下内容:

  • 第一颗星的位置

  • 笔触和填充颜色的调色板(每种四种颜色)

  • 最小和最大星点数

由于这是一种模式,因此应该定期重复变化的星形属性。例如,你会注意到点数从 5 开始,然后从一颗星到下一颗增加 1,直到达到 12 点。一旦发生这种情况,下一位明星将获得 5 分。

这些是我们需要转换为代码的模式属性。

设置模式属性

现在我们已经概述了模式的要求,让我们卷起袖子,用两个函数编写它的逻辑:

  • getPatternProps,它建立星形图案的属性。

  • init,它将遍历每个星星的位置,设置每个星星的属性,并调用 drawJS.drawStar 方法。

getPatternProps 函数将返回一个包含我们模式属性的对象。

function getPatternProps() {let patternProps = {start: {x: 50,y: 50,},numPoints: {min: 5,max: 12},space: {x: 100,y: 100},count: {x: 12,y: 6},fills: ['#b8a9c9', '#98d1d6', '#fed8b1', '#cff7d5'],strokes: ['#622569', '#4C858A', '#f06d06', '#008000']};return patternProps;
}

让我们来看看发生了什么:

  • start 将第一颗星的 x 和 y 坐标设置为 (50,50)。

start: { x:50, y:50 }
  • numPoints 将使星星可以拥有的最少点数设为 5,并将最大点数设置为 12。

numPoints: { min:5, max:12 }
  • space 将使星星在垂直和水平方向上相距 100 像素。

space: {x: 100, y: 100 }
  • count 告诉我们在一行 (x) 和六行 (y) 中有 12 颗星。

count: { x: 12, y: 6}
  • fills 将确定作为重复序列出现的四种填充颜色。

fills: ['#b8a9c9', '#98d1d6', '#fed8b1', '#cff7d5']
  • 笔画以相同的方式工作,使用数组来设置星形边框颜色的重复序列。

strokes: ['#622569', '#4C858A', '#f06d06', '#00ff00']

启动我们的 init 函数

我们的 init 函数将执行以下操作:

  • 设置图案的背景颜色

  • 检索模式属性

  • 遍历每个星位置

  • 设置每颗星的属性

  • 为每个星星调用 drawJS.drawStar

让我们启动我们的 init 函数并调用 setBackground,一个用于设置画布背景颜色的 drawJS 方法。

function init() {drawJS.setBackground('#215875');
}

此 setBackground方法使用 Canvas API 的 rect 和 fill 方法,并且是你将在库中看到的第一个方法。

现在,我们有了背景颜色,让我们通过调用 getPatternProps 来获取星形图案的属性。

function init() {drawJS.setBackground('#215875');let props = getPatternProps();
}

设置循环

在我们的 init 函数中,让我们创建一个填充星星的嵌套循环。我们将逐步建立这个。

让我们首先遍历行。我们可以使用 props.count.y 值访问行数。

let props = getPatternProps();
for (var y = 0; y < props.count.y; ++y) {
}

现在让我们看看 props.count.x 中每行的星星数。

for (var y = 0; y < props.count.y; ++y) {for (var x = 0; x < props.count.x; ++x) {}
}

我们现在拥有的这些 x 和 y 索引将派上用场。

这是我们的 init 函数的开始:

function init() {// set background on canvasdrawJS.setBackground('#215875');// get star propertieslet props = getPatternProps();// loop through rowsfor (var y = 0; y < props.count.y; ++y) {// loop through stars in a rowfor (var x = 0; x < props.count.x; ++x) {} }
}

好的,这是 init 函数的一个很好的开始。到目前为止,我们已经设置了我们的背景颜色,定义了我们的图案属性,并设置了我们的循环。

确定每个星星的位置

在这个嵌套循环中,让我们想象一下它正在绘制的当前星星。要确定这颗星星的位置,它必须使用 props 中的 start 和 space 属性。

我们的目标是计算恒星的中心坐标并将它们存储在两个变量中:cx 和 cy。

让我们从星星的左上角位置开始:props.start.x 和 props.start.y。那是在横幅的左上角,第一颗星星的中心所在。

接下来,让我们找出每颗星星相对于第一颗星星的位置应该在哪里。这取决于它在序列中的位置(x 和 y)以及星间距(props.space.x 和 props.space.y)。

要找出离第一个位置有多远,让我们将当前的 x 或 y 索引乘以相应的空间值。

props.space.x * x
props.space.y * y

下面的表达式将 props.start.x 添加到 props.space.x 和 x 的乘积,给我们当前恒星的水平位置。

let cx = props.start.x + props.space.x * x;

这个表达式以同样的方式为我们提供了垂直位置。

let cy = props.start.y + props.space.y * y;

我们现在可以获得每颗恒星的中心。

找出每颗星星的点数

在模式中,点数增加,直到达到 12。之后,我们回到 5。然后我们再次开始增加。但是我们如何确定每颗星星上的点数呢?为了找到这个,我们首先需要知道每个恒星在序列中的位置。

如果我们把整个模式看作一个数组,这个位置可以看作是星星的索引。

寻找明星指数

下面你可以看到每个星星的索引在模式的前两行中应该是什么。

我们可以在循环外声明一个计数器变量并每次递增它,但让我们用数学方法找到索引,以便我们可以更多地探索这些 x 和 y 值。

让我们首先找到当前星星所在行上方的行数,即 y。现在让我们将 y ,先前行的数量,乘以 props.count.x,每行的星星数。

y * props.count.x

如果我们当前的星星在第三排呢?

  • 我们知道 props.count.x 总是 12。

  • 在我们的第三行中,y 是 2。

  • 所以上面几行的星星数是 12 • 2 = 24。

然后我们应该添加 x,它是当前行中先前星的数量。然后我们可以用这个方程定义 star_idx。

let star_idx = x + props.count.x + y;

寻找点的范围

当我们从一颗星到下一颗时,我们希望点数从 5 增加到 12,然后从 5 重新开始并重复。

要知道何时从 5 点重新开始,我们必须知道我们可以拥有多少个不同的点数。所以让我们找出最小点数和最大点数之间的范围,包括 5 和 12。

在代码中,我们将称其为 diff。

let diff = props.numPoints.max - props.numPoints.min + 1;

由于 star.numPoints.max 是 12 并且 star.numPoints.min 是 5,因此在这种情况下 diff 最终为 8。

使用余数运算符

现在让我们找出每颗星星的点数。每颗星至少有五分,但有些可能有更多分。

为了计算出还有多少,我们将使用 num、星级索引和我们刚刚计算的差异。我们使用余数运算符来查找将 num 除以 diff 所得的余数。

let morePoints = num % diff;

morePoints 可以得到的最高值是 7。例如,15 除以8,我们将 15 除以 8 得到的余数是 7。如果我们增加到 16,那么 16 除以8 会重置为零。以下是两行中这种重复增加的情况:

为了获得当前星星上的点数,我们取 morePoints 并添加最小点数——props.numPoints.min——即 5。

let numPoints = props.numPoints.min + morePoints

以下是两行中点数的外观:

万岁!已找到每颗星上的点数。

使星星变小

为了使星星变小,让我们放置一个从 1 开始但在每行之后逐渐变小的乘数。

let multiplier = (props.count.y - y) / props.count.y;

因为我们的行索引 y 从零开始,props.count.y — y 在我们的第一行中是 6 — 0 或 6。

结果,props.count.y — 1 / props.count.y = 6/6 = 1。

但在第二行,y 是 1。

所以 props.count.y — y 是 6 — 1 或 5。

因此,如果我们四舍五入到最接近的百分位,props.count.y — 1 / props.count.y = 5/6 = 0.83。

我们现在可以使用乘数来调整星星的外半径和内半径,我们将分别初始化为 35 和 20。

let outerRadius = 35, innerRadius = 20;

现在让我们用乘数改变每一个。

outerRadius *= multiplier;
innerRadius *= multiplier;

完整的乘法器逻辑如下所示:

查找颜色、线宽和旋转

现在我们想要一个重复的颜色模式。请记住,我们有四种笔触颜色和四种填充颜色,正如我们在 stars 对象中的数组中所指定的:

fills: ['#b8a9c9', '#98d1d6', '#fed8b1', '#cff7d5'],
strokes: ['#622569', '#4C858A', '#f06d06', '#008000']

为了确定填充和笔画,我们将再次使用余数运算符,但这一次是为了确定笔画和填充数组中应该有什么索引,我们将假设它们具有相同的长度。我们将此值称为 color_idx。

let color_idx = num % props.fills.length

我们的 props.fills 数组的长度为 4,它成为我们的除数——所以我们可能的余数值在 0 到 3 之间。

为了获得填充和描边,我们使用 color_idx 索引到每个数组。

let fill = props.fills[color_idx];
let stroke = props.strokes[color_idx];

一旦一颗星星用数组中的最后一种颜色渲染,程序就会为下一颗星星使用第一种颜色。

最后,让我们设置 lineWidth 和rotate,这对每个星星都是一样的:

const lineWidth = 4, rotate = 0;

至此,我们需要的所有属性都已经设置好了!

回顾

让我们回顾一下我们现在拥有的九个属性。

  • cx 和 cy 是恒星中心点的坐标。

  • externalRadius 和innerRadius 决定了恒星的大小。

  • 填充和描边是星星的颜色。

  • 每颗星的线宽和旋转保持不变。

  • LnumPoints 确定每颗星上的点。

现在让我们将所有这些打包成一个对象,并将其直接传递给 drawStar 方法。

drawJS.drawStar({cx,cy,outerRadius,innerRadius,numPoints,lineWidth,stroke,fill,rotate
});

由于每个键的名称都与其在该对象中的值相同,因此我们可以对键和值使用一个术语——例如,cx 代替 cx:cx,rotate 代替rotate:rotate。

这是完成的 init 函数的样子。

function init() {// set background on canvasdrawJS.setBackground('#215875');// get star propertieslet props = getPatternProps();// loop through rowsfor (var y = 0; y < props.count.y; ++y) {// loop through columnsfor (var x = 0; x < props.count.x; ++x) {// determine star's center positionlet cx = props.start.x + props.space.x * x;let cy = props.start.y + props.space.y * y;// determine number of pointslet star_idx = x + props.count.x * y;let diff = props.numPoints.max - props.numPoints.min + 1;let morePoints = star_idx % diff;let numPoints = props.numPoints.min + morePoints;// determine star sizelet multiplier = (props.count.y - y) / props.count.ylet outerRadius = 35, innerRadius = 20;outerRadius *= multiplier;innerRadius *= multiplier;// determine star colorlet color_idx = star_idx % props.fills.length;let fill = props.fills[color_idx];let stroke = props.strokes[color_idx];// determine linewidth and rotationconst lineWidth = 4, rotate = 0;// call the methoddrawJS.drawStar({cx,cy,outerRadius,innerRadius,numPoints,lineWidth,stroke,fill,rotate});  } // end inner loop} // end outer loop
}

就是这样!这就是代码。下面是我们的演示,因此,你可以看到所有内容如何组合在一起。

查看演示地址:https://codepen.io/nevkatz/pen/jOmexaK

总结

恭喜你!到这里已完成本教程,你已经成功地使用了许多 JavaScript 概念以及相当多的数学来创建这个星形图案。如果你想了解这方面的更多信息,以下是我建议的一些后续步骤:

  • 修改 getPatternProps 以便你可以想出不同的模式。

  • 尝试为一颗星星制作动画——然后尝试将多颗星星排列成一个图案。

  • 尝试使用 Math.random() 和一些模式修改来创建夜空。

  • 希望你能从我的这篇文章中学会用 JavaScript 和 Canvas 绘制星星。

谢谢阅读!

学习更多技能

请点击下方公众号

如何使用 JavaScript 和 Canvas 创建星形图案相关推荐

  1. 如何在html5中实现多圆,JavaScript与html5如何实现canvas绘制圆形图案的方法介绍

    这篇文章主要介绍了js+html5实现canvas绘制圆形图案的方法,涉及html5图形绘制的基础技巧,需要的朋友可以参考下 本文实例讲述了js+html5实现canvas绘制圆形图案的方法.分享给大 ...

  2. 【JavaScript】Canvas绘图整理

    文章目录 绘制矩形 点线模式 绘制艺术字 绘制圆形路径 绘制圆角矩形 绘制凹多边形 绘制曲线 绘制位图 绘制坐标变换 绘制坐标变换与路径结合 绘制矩阵变换 绘制叠加效果 绘制线性渐变和径向渐变 位图填 ...

  3. 【JavaScript】Canvas绘制美丽的网螺旋旋转图形

    <!DOCTYPE html> <html> <head><meta http-equiv = "content-type" conten ...

  4. bubble html5,利用HTML5 Canvas创建交互式Bubble Chart

    Josh Marinacci也在其博客中详细地介绍了"如何利用HTML5 Canvas创建可以在移动桌面上运行的交互式图表以及如何利用真实数据填充图表."下面让我们跟随作者学习如何 ...

  5. html5怎么修改图片大小,HTML5 javascript修改canvas的大小

    方法1: 设定固定的值,这种方式跟在html中设定canvas的值没有什么区别: window.onload = function(){ canvas.height = 100; canvas.wid ...

  6. html 圆圈透明 其他不透明,Javascript HTML5 Canvas绘制透明圆圈

    我需要有一个函数在画布上绘制几个圆圈,从mousedown at center = x,y开始,并将鼠标拖动到deltaX,deltaY,从而为每个圆圈创建半径r.圆圈无法填充(需要它们透明),因此用 ...

  7. javascript学习-canvas

    canvas 简介 html5中canvas标签用于绘制图像.不过canvas标签本身没有绘制能力,是图形的容器,需要用脚本完成绘制.getContext()方法可返回一个对象,该对象提供了用于在画布 ...

  8. canvas使用滑杆交互_如何使用JavaScript和Canvas开发交互式文件上传器

    canvas使用滑杆交互 介绍 (Introduction) How nice or fun can we make the interactions on a website or web appl ...

  9. html5 放大镜效果,JavaScript+HTML5 canvas实现放大镜效果完整示例

    本文实例讲述了JavaScript+HTML5 canvas实现放大镜效果.分享给大家供大家参考,具体如下: 效果: www.jb51.net canvas放大镜 #copycanvas { bord ...

  10. JavaScript 使用Canvas绘图

    JavaScript  使用Canvas绘图 <canvas>元素负责在页面中设定一个区域,可以通过JavaScript动态地在这区域中绘制图形. 一.基本用法 1.先设置width和he ...

最新文章

  1. tensorflow2.0中dataset API 总结
  2. 嵌入式 linux 进程锁,嵌入式  Linux线程锁详解pthread_mutexattr_t
  3. 白白浪费了这满园春色
  4. expect自动化交互脚本(一)
  5. asp下载防盗链代码
  6. 【干货】周鸿祎谈雷军:能不能All In是一个核武器
  7. 高考辽宁理工高考计算机专业分数,辽宁高考985分数线2019-985大学在辽宁省的录取分数线及位次...
  8. AutoPep8-----Pycharm自动排版工具
  9. 【Java】split()和java.util.StringTokenizer分割字符串的性能比较
  10. 如何破解几乎所有的求职面试
  11. Python 机器学习在线指南
  12. tigervnc环境搭建
  13. [NVIDIA] Ubuntu 16.04 安装 nvidia-384 + cuda-9.0
  14. ATOM使用的一点心得与技巧——在一个窗口打开多个项目
  15. 半自动化与全自动化的区别
  16. Pandas数据分析groupby函数深度总结(1)
  17. linux基本安全防护
  18. IP协议协议--IP分片
  19. android控制创维电视,创维电视可以用手机控制吗
  20. http工具类发送post和get请求

热门文章

  1. 深度解读|盘扣销售价格上涨背后的原因是什么?
  2. trajan算法求lca 超级详细配图讲解
  3. 如何让MAC只显示外接屏幕!
  4. 微信小程序(1)--注册及下载IDE
  5. 相关系数(Correlation)
  6. 集线器、交换机、路由器
  7. protoc导出时遇到protoc-gen-go unable to determine Go import path解决方法
  8. c语言反步法编程,CCM模式下Boost电路的反步法非线性控制与仿真.pdf
  9. 正确的握笔姿势,握笔的姿势非常的重要
  10. vue+antDesign实现树形数据展示并表格父子级选中和取消联动