【问题标题】:SVG - Duplicate image along pathSVG - 沿路径复制图像
【发布时间】:2018-11-06 09:37:15
【问题描述】:

我目前正在尝试通过 SVG 和 javascript 创建自行车链条。

我尝试创建的结果将如下所示:

我遇到的问题是如何沿链的路径重复单个链环图像。我希望能够使用链接图像(例如: 然后沿着我定义的路径重复此操作。

我知道通过 SVG 沿路径重复文本是微不足道的,但是否可以对图像做同样的事情?复杂性来自于链环必须显示为一条连续线这一事实。

这是我目前所拥有的:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="UTF-8">
	</head>
	
	<body>
		<svg width="700" height="500">
			<path id="ChainPath" fill="none" stroke="red" d="M150 100 L400 100 C650 100 650 400 400 400 L150 400" />

			<text>
				<textPath href="#ChainPath" alignment-baseline="middle">
					0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
				</textPath>
			</text>
		</svg>
	</body>
</html>

任何帮助将不胜感激。 谢谢

【问题讨论】:

    标签: javascript html image svg


    【解决方案1】:

    我尝试实际实施 Robert Monfera 的答案,但发现了一些额外的问题。正如他所描述的,我开始计算位于各个链节的轴/连接销上的多边形的点,并且它们之间的距离完全相同。

    如果你看到它,你会立即感到震惊的是,链条不是一条平滑的线。对于人眼来说,它可能看起来像一个,但将围绕齿轮运行的链条描述为圆弧是一个不好的近似值。

    看问题中的图片。右齿轮有 15 个齿。这意味着相邻链节之间的角度为 24°,圆弧和绳索之间的点到点的长度差(即链节的结构长度)约为。 0.8%。这听起来并不多,但是对于您使用 50 像素的屏幕大小绘制的链接,它几乎是半个像素。这是可以看到的差异。

    下一个建议是使用&lt;marker&gt; 元素作为原点在两个销之间的中心的单个链节。由于上面定义的多边形的点位于引脚的位置,因此需要第二个连接所有多边形段的中点。

    在实现时,生成的链看起来像这样(我在顶部绘制了多边形来说明):

    在链条的“直”部分变形为“弯曲”部分的地方,链节严重错位。原因是标记方向在顶点处平分传入和传出切线。无论是直线部分还是沿着齿轮的弧线,都可以正常工作,但是在两者相遇的地方,显然不行。

    要使标记的方向正确,点处的切线必须与原始多边形的方向相匹配。罗伯特提议在两点之间画一条弧线。这个概率会起作用,但计算正确的弧半径和位置很复杂。

    我想出了一个方法,它可以生成一条看起来有点弯曲的平滑路径,但这条线最终永远不会显示出来,而且它的计算非常简单——我实际上是用 Excel 表做到的。

    假设我们有一个标记引脚位置的点列表:

    a    b    c    d    e    f    g ...
    

    有两种链接(正面和背面),所以每隔一个中间标记两点:

    a    b    c    d    e    f    g ...
      ab        cd        ef
    

    a, ab, c 在一条直线上,与c, cd, d 相同,以此类推。现在,如果您使用以下命令绘制路径

    <path d="M a L ab C b c cd d e ef f g ..." marker-mid="url(#link1)" />
    

    point a 不会有任何标记,因为它不在中间。定位标记的下一个顶点是ab, cd, ef, ...,而b, c, d, e, ... 是三次贝塞尔曲线的控制点。这意味着:在顶点ab 的切线路径是从a 到b 的直线,在cd 中从c 到d 的直线,等等。

    这是 Inkscape 的截图来说明:

    其他链接可以相应地描述为

    <path d="M b L bc C c d de e f fg g ..." marker-mid="url(#link2)" />
    

    如果想变得花哨,甚至可以使用S 命令将其缩短一点,该命令将控制点置于顶点之前并隐含地将其反射添加为之后的下一个控制点(点e, g, ... 是计算):

    <path d="M b L bc C c d de S f fg h hj ..." marker-mid="url(#link2)" />
    

    对于大结局,这是完成的图纸。只是为了它,我已经用相同的标记技术绘制了齿轮齿。

    <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 800 600" width="600" height="450">
      <defs>
        <marker id="blade1" markerUnits="userSpaceOnUse" orient="auto" overflow="visible">
          <path style="fill:#ececf4" d="M 16.5,-1.556 13.947,-1.78 A 14,14 0 0 1 -13.947,-1.78 L -16.5,-1.556 0,200 Z" />
          <path style="fill:none;stroke:#000" d="M 16.5,-1.556 13.947,-1.78 A 14,14 0 0 1 -13.947,-1.78 L -16.5,-1.556" />
        </marker>
        <marker id="blade2" markerUnits="userSpaceOnUse" orient="auto" overflow="visible">
          <path style="fill:#ececf4" d="M 16.5,0.507 13.694,-0.089 A 14,14 0 0 1 -13.694,-0.089 L -16.5,0.507 0,90 Z" />
          <path style="fill:none;stroke:#000" d="M 16.5,0.507 13.694,-0.089 A 14,14 0 0 1 -13.694,-0.089 L -16.5,0.507" />
        </marker>
        <path id="plate" d="M -9.689,-10.392 C -4.782 -7.56 4.782 -7.56 9.689,-10.392 A 12,12 0 1 1 9.689,10.392 C 4.782 7.56 -4.782 7.56 -9.689,10.392 A 12,12 0 1 1 -9.689,-10.392 Z" />
        <marker id="link1" style="stroke:#000" markerUnits="userSpaceOnUse" orient="auto" overflow="visible">
          <use xlink:href="#plate" style="fill:#ddd" />
          <circle id="pin" style="fill:#888;stroke:#000" cx="15.689" cy="0" r="4" />
          <use xlink:href="#pin" x="-31.378" />
        </marker>
        <marker id="link2" style="stroke:#000" markerUnits="userSpaceOnUse" orient="auto" overflow="visible">
          <use xlink:href="#plate" style="fill:#bbb" />
        </marker>
        <g id="center1">
          <circle r="80" cx="250" cy="300" />
          <g id="cq">
            <circle r="10" cx="345" cy="265.42" />
            <path d="M 252.984,200.049 A 100,100 0 0 1 324.557,233.367 25,25 0 0 0 361.957,200.182 150,150 0 0 0 254.883,150.086 25,25 0 0 0 252.984,200.049 Z" />
          </g>
          <use xlink:href="#cq" transform="rotate(90 250,300)" />
          <use xlink:href="#cq" transform="rotate(180 250,300)" />
          <use xlink:href="#cq" transform="rotate(270 250,300)" />
        </g>
        <mask id="m1">
          <rect fill="white" x="50" y="100" width="400" height="400" />
          <use xlink:href="#center1" fill="black" />
        </mask>
        <path id="center2" d="M 647.75 277.2 L 642.04 279.05 L 643.04 282.13 A 20 20 0 0 0 633.31 292.96 L 630.13 292.28 L 628.88 298.15 L 632.06 298.83 A 20 20 0 0 0 636.55 312.68 L 634.38 315.08 L 638.84 319.1 L 641.01 316.69 A 20 20 0 0 0 655.25 319.73 L 656.25 322.8 L 661.96 320.95 L 660.96 317.87 A 20 20 0 0 0 670.7 307.04 L 673.87 307.72 L 675.12 301.85 L 671.94 301.17 A 20 20 0 0 0 667.45 287.32 L 669.62 284.92 L 665.16 280.9 L 662.99 283.31 A 20 20 0 0 0 648.75 280.27 L 647.75 277.2 z" />
        <mask id="m2">
          <rect fill="white" x="572" y="220" width="160" height="160" />
          <use xlink:href="#center2" fill="black" />
        </mask>
      </defs>
      <g mask="url(#m1)">
        <path style="fill:none;marker-mid:url(#blade1)" d="M 430,300 427.27,331.26 419.14,361.56 405.88,390 387.89,415.7 365.7,437.89 340,455.88 311.56,469.14 281.26,477.27 250,480 218.74,477.27 188.44,469.14 160,455.88 134.3,437.89 112.11,415.7 94.12,390 80.86,361.56 72.73,331.26 70,300 72.73,268.74 80.86,238.44 94.12,210 112.11,184.3 134.3,162.11 160,144.12 188.44,130.86 218.74,122.73 250,120 281.26,122.73 311.56,130.86 340,144.12 365.7,162.11 387.89,184.3 405.88,210 419.14,238.44 427.27,268.74 430,300 427.27,331.26" />
      </g>
      <use xlink:href="#center1" style="fill:none;stroke:#000" />
      <g mask="url(#m2)">
        <path style="fill:none;marker-mid:url(#blade2)" d="M 578.19,315.69 V 284.31 L 590.95,255.65 614.27,234.65 644.11,224.95 675.32,228.23 702.49,243.92 720.94,269.31 727.46,300 720.94,330.69 702.49,356.08 675.32,371.77 644.11,375.05 614.27,365.35 590.95,344.35 578.19,315.69 V 284.31"/>
      </g>
      <use xlink:href="#center2" style="fill:none;stroke:#000" />
      <path style="fill:none;marker-mid:url(#link2)" d="M 281.26 477.27 L 265.63 478.63 C 250 480 218.74 477.27 203.59 473.21 S 160 455.88 147.15 446.89 112.11 415.7 103.11 402.85 80.86 361.56 76.79 346.41 70 300 71.37 284.37 80.86 238.44 87.49 224.22 112.11 184.3 123.21 173.21 160 144.12 174.22 137.49 218.74 122.73 234.37 121.37 281.26 122.73 296.41 126.79 341.88 138.97 357.03 143.03 402.5 155.2 417.66 159.26 463.13 171.43 478.28 175.49 523.75 187.66 538.91 191.72 584.38 203.89 599.54 207.95 645.01 220.12 660.16 224.18 702.49 243.92 711.71 256.61 727.46 300 724.2 315.35 702.49 356.08 688.91 363.92 645.01 379.88 629.85 383.94 584.38 396.11 569.22 400.17 523.75 412.34 508.6 416.4 463.13 428.57 447.97 432.63 402.5 444.8 387.35 448.86 341.88 461.03 326.72 465.09 281.26 477.27 265.63 478.63 L 250 480" />
      <path style="fill:none;marker-mid:url(#link1)" d="M 311.56 469.14 L 296.41 473.21 C 281.26 477.27 250 480 234.37 478.63 S 188.44 469.14 174.22 462.51 134.3 437.89 123.21 426.79 94.12 390 87.49 375.78 72.73 331.26 71.37 315.63 72.73 268.74 76.79 253.59 94.12 210 103.11 197.15 134.3 162.11 147.15 153.11 188.44 130.86 203.59 126.79 250 120 265.63 121.37 311.56 130.86 326.72 134.91 372.19 147.08 387.35 151.14 432.82 163.31 447.97 167.37 493.44 179.54 508.6 183.6 554.07 195.77 569.22 199.83 614.69 212 629.85 216.06 675.32 228.23 688.91 236.08 720.94 269.31 724.2 284.65 720.94 330.69 711.71 343.39 675.32 371.77 660.16 375.82 614.69 388 599.54 392.05 554.07 404.23 538.91 408.28 493.44 420.46 478.28 424.51 432.82 436.69 417.66 440.74 372.19 452.92 357.03 456.97 311.56 469.14 296.41 473.21 L 281.26 477.27" />
    </svg>

    【讨论】:

      【解决方案2】:

      在 SVG 中,我定义了一个链环并根据需要创建尽可能多的副本(参考点位于链环的中心):

      <use class="chain chainlink1" xlink:href="#chainlink" />
      <use class="chain chainlink2" xlink:href="#chainlink" />
      <use class="chain chainlink3" xlink:href="#chainlink" />
      <use class="chain chainlink4" xlink:href="#chainlink" />
      ....
      

      在css中,我定义了要遵循的路径

      .chain {
         offset-path: path("m 50,150 c -2,-19 12,-36 24,-44 13,-8 176,-54 226,-56 
                            60,0 100,49.17776 100,100 0,51 -42,99 -100,99 C 262,250 
                            95,206 72,191 58,182 50,166 50,150 Z");
         animation: cycle 20s linear infinite;
      }
      
      @keyframes cycle {
        0% { 
          offset-distance: 0%;
        }
        100% { 
           offset-distance: 100%;
        }
      }
      

      以及每个chainlink的延迟

      .chainlink2 {
        animation-delay: 1s;
      }
      .chainlink3 {
        animation-delay: 2s;
      }
      ...
      

      持续时间和延迟取决于链环的长度、路径...和所需的速度

      【讨论】:

        【解决方案3】:

        这是一种解决方案:只要您愿意进行数学运算以确保多边形点之间的距离与轴到点完全匹配,您就可以使用 &lt;marker&gt; 元素制作 &lt;polyline&gt; 或 &lt;polygon&gt; - 链元素的轴距离(即,它不是元素的总长度,而是重要的重复放置距离)。它需要使用a^2 + b^2 = c^2,其中c 是这个间距(一个常数),a 和b 分别是到前一点的水平和垂直距离。也就是说,你需要计算出链轴中心的x,y坐标(*)(如果你这样做,那么你还不如将它们一个一个放置,但是你会有很多DOM元素) .

        您将需要orient="auto" 将链元素与多边形对齐,并使用属性viewBox、refX、refY、markerWidth、markerHeight 以便其中一个轴链元素与多边形点完美对齐(然后,通过使用上述毕达哥拉斯定理,另一个链元素中心将对应于下一个多边形点)。

        以下是&lt;marker&gt; 的一些示例:

        当我查看您的链条(普通自行车链条)时,最好在彼此顶部制作两个几乎相同的多边形,两者之间有半节距偏移:

        • 在背景中,部分被遮挡的链元素
        • 在前景中,完全可见的 8 形链板

        因此,只要您的点在正确的位置,您的连锁店就会是无缝的。您甚至可以为动画链设置相邻元素之间的过渡循环:-)(仅通过链间距进行过渡就足够了,因为链间距转换将使您回到无法区分的视图)

        (*) 正如 ccprog 指出的below,为了使各个链元素更好地对齐,它不是轴中心,而是链中心必须位于多边形上。如果有急转弯,这可能会使数学变得更加乏味,但在这种特定情况下(自行车链条),圆半径似乎足够大,不会导致链条元素明显分离。

        【讨论】:

        • 啊,还有一件事:您可以使用getPointAtLength 以这种方式帮助点位置: 1. 使用线条、弧线渲染形状,即。通过&lt;path&gt; 元素;然后通过调用getPointAtLength 以固定节奏获取 x、y 坐标。 &lt;path&gt; 不需要显示,但无论如何它都会被遮挡。 bl.ocks.org/mbostock/1705868
        • 虽然这是一个好的开始,但您有一个假设是错误的:标记方向bisects the incoming and outgoing tangent at a vertex。这意味着如果您将标记原点设置在一个轴上,另一个将在曲线之外结束,其数量取决于链元素之间的角度。相反,将标记原点设置为链节的中间,并将多边形重绘为一条路径,其顶点位于多边形点之间的中间,并平滑曲线以使切线与多边形段平行。
        • “圆的半径似乎足够大,不会导致链元素明显分离” - 不幸的是,事实并非如此。我已经涉足并使用多边形会导致直线段和曲线段相交的非常明显的障碍。你真的需要那些正确的切线。
        • SVG path 迷你语言中的弧a/A 似乎可以直接使用圆弧,可能需要进行更正,因为弦比给定的弧略短圆角和半径,取决于参数。 IE。弧应该形成一个圆,该圆适合由链节轴形成的多边形。
        • 正确,但计算起来相对复杂。既然您似乎感兴趣,我将发布我的实际实验作为第二个答案。
        猜你喜欢
        • 2021-09-09
        • 2011-06-29
        • 2016-02-01
        • 2018-09-05
        • 1970-01-01
        • 1970-01-01
        • 2016-03-18
        • 2011-08-20
        • 2016-07-18
        相关资源
        最近更新 更多