×

如何在HTML5 SVG上绘制三次贝塞尔曲线

作者:Terry2020.12.29来源:Web前端之家浏览:6931评论:0

600.png

文章“如何在SVG中创建复杂路径”对该<path>元素进行了研究,并展示了如何绘制一系列直线和圆弧以创建任何形状。(它通常用于复制字体,而无需下载完整的字体。)

d属性提供了一些额外的技巧来绘制平滑曲线。在本文中,我们将讨论三次贝塞尔曲线,但您也可以参考“如何在SVG图像上绘制二次贝塞尔曲线”中的一个简单选项。

什么是三次贝塞尔曲线?

您可能在桌面发布和图形包中遇到了三次贝塞尔曲线。它们定义了起点(P0)和终点(P3)。但是,虽然二次曲线使用一个控制点,三次贝塞尔曲线却具有两个:曲线的每一端(P1P2)一条。

路径难题

三次贝塞尔曲线是使用C路径d属性中的指令定义的:

<path d="M100,250 C100,100 400,100 400,250" />

初始M指令将笔移动到第一点(100,250)。三个坐标遵循C:第一个控制点(100,100),第二个控制点(400,100)和最终终点(400,250)。

您也可以使用小写字母c表示相对坐标,而不是绝对坐标。以下曲线将是相同的,并且可能更容易编写:

<path d="M100,250 c0,-150 300,-150 300,0" />

最后,还有速记Ss指令(通常,小写选项表示相对坐标,而不是绝对坐标)。它们通过设置另一个终点及其关联的控制点,接受另外两个坐标以将多条曲线串在一起。假定起点控制点与上一条曲线上的终点控制点相同。例如,采用以下路径:

<path d="M100,250 C100,100 400,100 400,250 S700,400 700,250" />

如上所述,它绘制了从100,250(控制点的100,100)到400,250(控制点的400,100)的曲线。然后从400,250(控制点在不变400,100)到700,250(控制点在700,400)绘制另一条曲线:

q1.jpg

三次贝塞尔曲线可能很难编码和可视化,因此此快速生成工具将为您生成<path>代码:

<!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8">  <link rel="apple-touch-icon" type="image/png" href="https://cpwebassets.codepen.io/assets/favicon/apple-touch-icon-5ae1a0698dcc2402e9712f7d01ed509a57814f994c660df9f7a952f3060705ee.png" /><meta name="apple-mobile-web-app-title" content="CodePen">
<link rel="shortcut icon" type="image/x-icon" href="https://cpwebassets.codepen.io/assets/favicon/favicon-aec34940fbc1a6e787974dcd360f2c6b63348d4b1f4e06c77743096d55480f33.ico" />
<link rel="mask-icon" type="" href="https://cpwebassets.codepen.io/assets/favicon/logo-pin-8f3771b1072e3c38bd662872f6b673a722f4b3ca2421637d5596661b4e2132cc.svg" color="#111" />

  <meta charset="utf-8">  <meta name='viewport' content='width=device-width, initial-scale=1'>
  <title>CodePen - SVG cubic bézier curve path creation tool</title>
  <link rel="stylesheet" media="screen" href="https://cpwebassets.codepen.io/assets/fullpage/fullpage-4de243a40619a967c0bf13b95e1ac6f8de89d943b7fc8710de33f681fe287604.css" />  <link href="https://fonts.googleapis.com/css?family=Lato:300,400,400italic,700,700italic,900,900italic&display=swap" rel="stylesheet" />
  <link rel="apple-touch-icon" type="image/png" href="https://cpwebassets.codepen.io/assets/favicon/apple-touch-icon-5ae1a0698dcc2402e9712f7d01ed509a57814f994c660df9f7a952f3060705ee.png" /><meta name="apple-mobile-web-app-title" content="CodePen">
<link rel="shortcut icon" type="image/x-icon" href="https://cpwebassets.codepen.io/assets/favicon/favicon-aec34940fbc1a6e787974dcd360f2c6b63348d4b1f4e06c77743096d55480f33.ico" />
<link rel="mask-icon" type="" href="https://cpwebassets.codepen.io/assets/favicon/logo-pin-8f3771b1072e3c38bd662872f6b673a722f4b3ca2421637d5596661b4e2132cc.svg" color="#111" />



  <title>CodePen - SVG cubic bézier curve path creation tool</title>  <script>  if (document.location.search.match(/type=embed/gi)) {    window.parent.postMessage("resize", "*");  }</script>
</head>
<body class="">
  <div id="result-iframe-wrap" role="main">
    <iframe      id="result"      srcdoc="<!DOCTYPE html><html lang=&quot;en&quot; >
<head>
  <meta charset=&quot;UTF-8&quot;>  <link rel=&quot;apple-touch-icon&quot; type=&quot;image/png&quot; href=&quot;https://cpwebassets.codepen.io/assets/favicon/apple-touch-icon-5ae1a0698dcc2402e9712f7d01ed509a57814f994c660df9f7a952f3060705ee.png&quot; /><meta name=&quot;apple-mobile-web-app-title&quot; content=&quot;CodePen&quot;>
<link rel=&quot;shortcut icon&quot; type=&quot;image/x-icon&quot; href=&quot;https://cpwebassets.codepen.io/assets/favicon/favicon-aec34940fbc1a6e787974dcd360f2c6b63348d4b1f4e06c77743096d55480f33.ico&quot; />
<link rel=&quot;mask-icon&quot; type=&quot;&quot; href=&quot;https://cpwebassets.codepen.io/assets/favicon/logo-pin-8f3771b1072e3c38bd662872f6b673a722f4b3ca2421637d5596661b4e2132cc.svg&quot; color=&quot;#111&quot; />

  <title>CodePen - SVG cubic bézier curve path creation tool</title>        <style>*, *:before, *:after {  box-sizing: border-box;  padding: 0;  margin: 0;}
html {  height: 100%;}
body {  height: 100%;  font-family: Lato, helvetica, sans-serif;  font-size: 100%;  padding: 0;  margin: 0;  color: #333;  background-color: #fff;  overflow: hidden;}
#mysvg {  display: block;  width: auto;  height: 100%;  margin: 0;  background: radial-gradient(ellipse at center, #fefefe 0%, #cbeeff 100%);  border: 5px solid #333;  touch-action: none;}
@media (max-aspect-ratio: 1/1) {
  #mysvg {    width: 100%;    height: auto;  }
}
#mysvg path {   stroke-width: 10;   stroke: #000;   stroke-linecap: round;   fill: none;}
#mysvg path.fill {   fill: #3ff;}
#mysvg .control {   stroke-width: 3;   stroke: #c00;   fill: #fff;}
#mysvg .control:hover, #mysvg .control.drag{   fill: #c00;   cursor: move;}
#mysvg line{   stroke-width: 2;   stroke: #999;   stroke-linecap: round;   stroke-dasharray: 5,5;}
#output {  position: fixed;  bottom: 10px;  right: 10px;  padding: 0.2em;  background-color: rgba(255,255,255,0.9);  border-radius: 5px;}
h1 {  font-size: 1.2em;}
#path {  font-family: monospace;  font-size: 1em;  padding: 0.3em;  border: 1px solid #999;  user-select: all;}</style>
  <script>  window.console = window.console || function(t) {};</script>
      <script>  if (document.location.search.match(/type=embed/gi)) {    window.parent.postMessage(&quot;resize&quot;, &quot;*&quot;);  }</script>

</head>
<body translate=&quot;no&quot; >  <svg xmlns=&quot;http://www.w3.org/2000/svg&quot; id=&quot;mysvg&quot; viewBox=&quot;0 0 500 500&quot; preserveAspectRatio=&quot;xMidYMid meet&quot;>
  <title>SVG curve</title>  <desc>example curves in SVG</desc>
  <circle id=&quot;p1&quot; cx=&quot;100&quot; cy=&quot;250&quot; r=&quot;30&quot; class=&quot;control&quot; />  <circle id=&quot;p2&quot; cx=&quot;400&quot; cy=&quot;250&quot; r=&quot;30&quot; class=&quot;control&quot; />
  <circle id=&quot;c1&quot; cx=&quot;100&quot; cy=&quot;100&quot; r=&quot;20&quot; class=&quot;control&quot; />  <circle id=&quot;c2&quot; cx=&quot;400&quot; cy=&quot;100&quot; r=&quot;20&quot; class=&quot;control&quot; />
  <line id=&quot;l1&quot; x1=&quot;100&quot; y1=&quot;250&quot; x2=&quot;100&quot; y2=&quot;100&quot; />  <line id=&quot;l2&quot; x1=&quot;400&quot; y1=&quot;250&quot; x2=&quot;400&quot; y2=&quot;100&quot; />
  <path id=&quot;curve&quot; d=&quot;M100,250 C100,100 400,100 400,250&quot; />
</svg>
<div id=&quot;output&quot;>  <h1>SVG Quadratic B&amp;eacute;zier Curve</h1>  <p id=&quot;path&quot;></p></div>    <script src=&quot;https://cpwebassets.codepen.io/assets/common/stopExecutionOnTimeout-157cd5b220a5c80d4ff8e0e70ac069bffd87a61252088146915e8726e5d9f147.js&quot;></script>
        <script id=&quot;rendered-js&quot; >// initializeconstsvg = document.getElementById('mysvg'),NS = svg.getAttribute('xmlns'),vb = svg.getAttribute('viewBox').split(' ').map(v => +v),box = {  xMin: vb[0], xMax: vb[0] + vb[2] - 1,  yMin: vb[1], yMax: vb[1] + vb[3] - 1 };
node = {};
'p1,p2,c1,c2,l1,l2,curve,path'.split(',').map(s => {  node[s] = document.getElementById(s);});
// eventssvg.addEventListener('pointerdown', dragHandler);document.addEventListener('pointermove', dragHandler);document.addEventListener('pointerup', dragHandler);
drawCurve();

// drag handlerlet drag;function dragHandler(event) {
  event.preventDefault();
  const  target = event.target,  type = event.type,  svgP = svgPoint(svg, event.clientX, event.clientY);
  // fill toggle  if (!drag &amp;&amp; type === 'pointerdown' &amp;&amp; target === node.curve) {
    node.curve.classList.toggle('fill');    drawCurve();
  }
  // start drag  if (!drag &amp;&amp; type === 'pointerdown' &amp;&amp; target.classList.contains('control')) {
    drag = {      node: target,      start: getControlPoint(target),      cursor: svgP };

    drag.node.classList.add('drag');
  }
  // move element  if (drag &amp;&amp; type === 'pointermove') {
    updateElement(    drag.node,    {      cx: Math.max(box.xMin, Math.min(drag.start.x + svgP.x - drag.cursor.x, box.xMax)),      cy: Math.max(box.yMin, Math.min(drag.start.y + svgP.y - drag.cursor.y, box.yMax)) });


    drawCurve();
  }
  // stop drag  if (drag &amp;&amp; type === 'pointerup') {
    drag.node.classList.remove('drag');    drag = null;
  }
}

// translate page to SVG co-ordinatefunction svgPoint(element, x, y) {
  var pt = svg.createSVGPoint();  pt.x = x;  pt.y = y;  return pt.matrixTransform(element.getScreenCTM().inverse());
}

// update elementfunction updateElement(element, attr) {
  for (a in attr) {    let v = attr[a];    element.setAttribute(a, isNaN(v) ? v : Math.round(v));  }
}

// get control point locationfunction getControlPoint(circle) {
  return {    x: Math.round(+circle.getAttribute('cx')),    y: Math.round(+circle.getAttribute('cy')) };

}

// update curvefunction drawCurve() {
  const  p1 = getControlPoint(node.p1),  p2 = getControlPoint(node.p2),  c1 = getControlPoint(node.c1);  c2 = getControlPoint(node.c2);
  // control line 1  updateElement(  node.l1,  {    x1: p1.x,    y1: p1.y,    x2: c1.x,    y2: c1.y });


  // control line 2  updateElement(  node.l2,  {    x1: p2.x,    y1: p2.y,    x2: c2.x,    y2: c2.y });


  // curve  const  d = `M${p1.x},${p1.y} C${c1.x},${c1.y} ${c2.x},${c2.y} ${p2.x},${p2.y}` + (  node.curve.classList.contains('fill') ? ' Z' : '');
  updateElement(node.curve, { d });
  node.path.textContent = `<path d=&quot;${d}&quot; />`;
}//# sourceURL=pen.js    </script>
  
</body>
</html> "      sandbox="allow-downloads allow-forms allow-modals allow-pointer-lock allow-popups allow-presentation  allow-scripts allow-top-navigation-by-user-activation" allow="accelerometer; camera; encrypted-media; geolocation; gyroscope; microphone; midi" allowTransparency="true"      allowpaymentrequest="true" allowfullscreen="true" class="result-iframe">      </iframe>
  </div></body></html>

相应地将控制点拖到曲线的两端。单击曲线本身可切换填充效果,以添加结束Z指令。

请注意,此工具必须将DOM页面坐标转换为SVG坐标,以确保它可以在所有屏幕尺寸下使用。这可能比您预期的要复杂一些,因此请参阅“如何从DOM转换为SVG坐标并再次返回”。

如果您想要一个更简单的选项,请尝试在SVG图像上创建二次Bézier曲线。


您的支持是我们创作的动力!
温馨提示:本文作者系Terry ,经Web前端之家编辑修改或补充,转载请注明出处和本文链接:
https://jiangweishan.com/article/svg20201229ad1.html

网友评论文明上网理性发言 已有0人参与

发表评论: