Two.js 一个api,可以很容易地用代码创建 2D 形状。接下来,您将学习如何使用javascript创建形状并为其设置动画。
Two.js 与渲染器无关,因此您可以依赖相同的 API 来使用 canvas、svg 或 WebGL 进行绘制。该库有很多方法可用于控制不同形状在屏幕上的显示方式或动画方式。
安装
该库的未压缩版本大小约为 128 KB,而压缩版本为 50 KB。如果您使用的是最新版本,则可以使用自定义构建进一步减小库的大小。
您可以从 GitHub 下载该库的缩小版本,也可以直接链接到CDN 托管版本。将库添加到网页后,您就可以开始绘制不同的形状或对象并为其制作动画。
创建基本形状
首先,您需要告诉 Two.js 您要在其上绘制图形并为其设置动画的元素。您可以将一些参数传递给构造Two
函数来进行设置。
type
使用该属性设置渲染器的类型。您可以指定一个值,如svg
、webgl
、canvas
等。默认情况下type
设置为svg
。绘图空间的宽度和高度可以使用width
和height
参数指定。您还可以使用fullscreen
参数将绘图空间设置为可用的完整屏幕。当fullscreen
设置为 true 时, 和 的值width
将height
被忽略。
autostart
最后,您可以告诉 Two.js 在布尔参数的帮助下自动启动动画。
After passing all the desired parameters to the constructor, you can start drawing lines, rectangles, circles, and ellipses.
You can draw a line using two.makeLine(x1, y1, x2, y2)
. Here, (x1, y1)
are the coordinates of the first end point, and (x2, y2)
are the coordinates of the second end point. This function will return a Two.Line
object, which can be stored in a variable for further manipulation at a later point.
以类似的方式,您可以分别使用two.makeRectangle(x, y, width, height)
和绘制普通矩形和圆角矩形two.makeRoundedRectangle(x, y, width, height, radius)
。请记住这一点x
并y
确定矩形的中心,而不是像许多其他库那样确定其左上角坐标。width
和height
参数将确定矩形的大小。该radius
参数用于指定圆角的半径值。
您还可以分别使用two.makeCircle(x, y, radius)
和在网页上渲染圆形和椭圆形two.makeEllipse(x, y, width, height)
。就像矩形一样,x
和y
参数指定圆或椭圆的中心。在椭圆的情况下将width
and设置为相同的值会将其渲染为圆形。height
Two.js 中您将经常使用的一种有用方法是two.makeGroup(objects)
. 您可以传递不同对象的列表,也可以将对象、路径或组的数组作为参数传递给此方法。它还将返回一个Two.Group
对象。
操作组中的对象
创建组后,您可以使用该组提供给您的属性一次操作其所有子项。
和属性可用于设置组中所有子项的笔触和填充颜色stroke
。fill
他们将接受您可以在css中表示颜色的所有有效形式。这意味着您可以自由使用 RGB、HSL 或十六进制表示法。您也可以简单地使用颜色名称,如 orange
、red
或 blue
。同样,您可以为所有其他属性设置值,例如linewidth
、opacity
、miter
和cap
。noFill()
可以使用和noStroke()
方法从组中的所有子项中删除填充和描边。
您还可以应用其他物理变换,例如scale
、rotation
和translation
。这些转换将应用于单个对象。使用 和 之类的方法可以轻松地将新对象添加到组中并删除add()
它们remove()
。
定义渐变和书写文本
您可以在 Two.js 中定义线性和径向渐变。定义渐变并不意味着它会在屏幕上自动渲染,但它可以供您在设置各种对象的fill
或值时使用。stroke
您可以使用 定义线性渐变two.makeLinearGradient(x1, y1, x2, y2, stops)
。值x1
和y1
确定渐变开始的坐标。同样,数值x2
和y2
确定渐变结束的坐标。该stops
参数是一个Two.Stop
实例数组。这些定义了数组每个部分的颜色以及每种颜色过渡到下一种颜色的位置。它们可以使用 来定义 new Two.Stop(offset, color, opacity)
,其中offset
确定了渐变上必须完全渲染特定颜色的点。该color
参数确定特定点的渐变颜色。您可以使用任何有效的 CSS 颜色表示作为其值。最后,opacity
参数确定颜色的不透明度。不透明度是可选的,它可以是 0 到 1 之间的任何值。
您可以使用类似的方式定义径向渐变two.makeRadialGradient(x, y, radius, stops, fx, fy)
。在这种情况下,这些值x
决定y
了渐变的中心。该radius
参数指定渐变应该延伸多远。您还可以将一组停靠点传递给此方法,以设置渐变的颜色组合位置。参数fx
和fy
是可选的,它们可以用来指定渐变的焦点位置。
在下面的 CodePen 中查看一些渐变类型及其代码。
请记住,渐变的位置x
和y
位置是相对于它们尝试填充的形状的原点。例如,应该从中心填充形状的径向渐变将始终具有x
并y
设置为零。
Two.js 还允许您在绘图区域上编写文本,并在以后根据您的需要进行更新。这需要使用方法two.makeText(message, x, y, styles)
。从参数名称中可以明显看出这message
是您要编写的实际文本。参数x
和y
是作为书写文本中心点的坐标。参数是一个对象,styles
可用于设置大量属性的值。
您可以使用样式来设置诸如 font family
、size
和等属性的值alignment
。您还可以指定fill
、stroke
、opacity
、rotation
、scale
和等属性的值translation
。
创建 Two.js 项目
在了解了所有这些方法和属性之后,是时候将它们应用到项目中了。在本教程中,我将向您展示如何使用 Two.js 渲染元素周期表的前十个元素,其中电子围绕原子核旋转。原子核也会有一些轻微的运动,以提高我们表示的视觉吸引力。
我们首先定义一些稍后会用到的变量和函数。
var centerX = window.innerWidth / 2; var centerY = window.innerHeight / 2; var elem = document.getElementById("atoms"); var elementNames = [ "", "Hydrogen", "Helium", "Lithium", "Beryllium", "Boron", "Carbon", "Nitrogen", "Oxygen", "Fluorine", "Neon" ]; var styles = { alignment: "center", size: 36, family: "Lato" }; var nucleusCount = 10; var nucleusArray = Array(); var electronCount = 10; var electronArray = Array(); function intRange(min, max) { return Math.random() * (max - min) + min; }
上面的代码将我们窗口中心的坐标存储在变量centerX
和centerY
中。这些将在稍后用于将我们的原子置于中心。该 elementNames
数组包含元素周期表前十个元素的名称。每个名称的索引对应于该元素的电子和质子数,并以空字符串开头。该styles
对象包含用于设置文本对象样式的属性。
我们还定义了一个函数intRange()
来获取给定极端值内的随机整数值。
var two = new Two({ fullscreen: true }).appendTo(elem); var protonColor = two.makeRadialGradient( 0, 0, 15, new Two.Stop(0, "red", 1), new Two.Stop(1, "black", 1) ); var neutronColor = two.makeRadialGradient( 0, 0, 15, new Two.Stop(0, "blue", 1), new Two.Stop(1, "black", 1) ); for (i = 0; i < nucleusCount; i++) { nucleusArray.push(two.makeCircle(intRange(-10, 10), intRange(-10, 10), 8)); } nucleusArray.forEach(function(nucleus, index) { if (index % 2 == 0) { nucleus.fill = protonColor; } if (index % 2 == 1) { nucleus.fill = neutronColor; } nucleus.noStroke(); });
这将创建一个 Two 的实例并定义两个径向渐变。红/黑径向渐变代表质子,蓝/黑渐变代表中子。
我们使用该intRange()
函数将所有这些中子和质子放置在彼此相距 20 像素的范围内。该makeCircle()
方法还将这些质子和中子的半径设置为 10 个像素。之后,我们迭代nucleusArray
并交替用不同的渐变填充每个圆圈。
for (var i = 0; i < 10; i++) { if (i < 2) { var shellRadius = 50; var angle = i * Math.PI; electronArray.push( two.makeCircle( Math.cos(angle) * shellRadius, Math.sin(angle) * shellRadius, 5 ) ); } if (i >= 2 && i < 10) { var shellRadius = 80; var angle = (i - 2) * Math.PI / 4; electronArray.push( two.makeCircle( Math.cos(angle) * shellRadius, Math.sin(angle) * shellRadius, 5 ) ); } }
Placing neutrons and protons inside the nucleus was easy. However, properly placing the electrons at a uniform distance will require a little maths. We use the shellRadius
variable to specify the distance of different electron shells from the nucleus. A whole circle covers an angle equal to 2 PI radians. We can place different electrons uniformly by distributing the 2 PI radians between them equally.
Math.cos()
和Math.sin()
函数用于根据不同电子的角度来分离不同电子的位置矢量的垂直和水平分量。
var orbitA = two.makeCircle(centerX, centerY, 50); orbitA.fill = "transparent"; orbitA.linewidth = 2; orbitA.stroke = "rgba(0, 0, 0, 0.1)"; var orbitB = two.makeCircle(centerX, centerY, 80); orbitB.fill = "transparent"; orbitB.linewidth = 2; orbitB.stroke = "rgba(0, 0, 0, 0.1)"; var groupElectronA = two.makeGroup(electronArray.slice(0, 2)); groupElectronA.translation.set(centerX, centerY); groupElectronA.fill = "orange"; groupElectronA.linewidth = 1; var groupElectronB = two.makeGroup(electronArray.slice(2, 10)); groupElectronB.translation.set(centerX, centerY); groupElectronB.fill = "yellow"; groupElectronB.linewidth = 1; var groupNucleus = two.makeGroup(nucleusArray); groupNucleus.translation.set(centerX, centerY);
这部分代码将来自不同壳层的电子以及中子和质子放在各自不同的组中。它还同时为特定轨道上的所有电子设置填充颜色。
two .bind("update", function(frameCount) { groupElectronA.rotation += 0.025 * Math.PI; groupElectronB.rotation += 0.005 * Math.PI; groupNucleus.rotation -= 0.05; }) .play(); var text = two.makeText("", centerX, 100, styles); nucleusArray.forEach(function(nucleus, index) { nucleus.opacity = 0; }); electronArray.forEach(function(electron, index) { electron.opacity = 0; });
这部分代码将单个电子和质子的不透明度设置为零。它还告诉 Two.js 以特定速度旋转电子和质子。
visible = 0; document.addeventListener("click", function(event) { if (visible < nucleusArray.length) { nucleusArray[visible].opacity = 1; electronArray[visible].opacity = 1; visible++; text.value = elementNames[visible]; } else { nucleusArray.forEach(el => el.opacity=0); electronArray.forEach(el => el.opacity=0); visible = 0; text.value = elementNames[0]; } });
代码的最后一部分允许我们通过单击鼠标或点击来遍历元素。为了加载下一个元素,我们让一个电子和一个质子或中子可见,并更新元素名称。单击最后一个元素后,所有粒子再次隐藏,因此我们可以重新开始。该visible
变量跟踪当前可见的原子粒子的数量,以便我们可以相应地显示或隐藏它们。
尝试在以下CodePen 演示中单击或点击以查看元素周期表的前十个元素。
最后的想法
我们在本教程开始时简要介绍了 Two.js 库以及如何使用它来绘制矩形、圆形和椭圆等形状。之后,我们讨论了如何将不同的对象组合在一起以同时操作它们。我们使用这种能力对元素进行分组,以同步平移和旋转它们。这些工具都汇集在我们对元素周期表中前十个元素的原子的动画中。