是否可以使用当前的MDN画布2DAPI函数在userSpaceOnUse坐标中使用gradientTransforms渲染SVG径向渐变?

我有一个 SVG:

<svg height="781.8" width="1077.15" xmlns="http://www.w3.org/2000/svg">
    <radialGradient cx="0" cy="0" gradientTransform="matrix(-0.0664, 0.0141, 0.0063, 0.0288, 137.9, -123.45)" gradientUnits="userSpaceOnUse" r="819.2" spreadMethod="pad">
      <stop offset="0.0" stop-color="#ff0000" stop-opacity="0.34901962"/>
      <stop offset="1.0" stop-color="#ff0000" stop-opacity="0.0"/>
    </radialGradient>
    <g transform="translate(309.85, 414.55) rotate(0, 600, 300)">
       <path transform="translate(-125, -10)" d="M161.0 -139.65 L160.25 -139.45 160.8 -139.85 Q161.55 -140.4 163.6 -140.15 L161.0 -139.65 M92.55 -115.35 Q91.1 -122.1 97.5 -128.9 107.45 -139.25 134.7 -146.75 L142.55 -148.35 Q147.4 -149.25 152.3 -148.3 L160.45 -147.3 161.4 -147.25 159.25 -145.4 Q152.35 -139.0 152.35 -135.5 L152.4 -134.75 152.4 -134.6 151.85 -132.55 151.6 -131.55 151.65 -131.45 Q152.45 -130.95 153.3 -131.0 L153.55 -130.1 Q153.55 -129.7 153.7 -129.45 154.0 -128.9 155.05 -128.9 158.8 -128.9 162.2 -130.4 164.8 -131.5 165.55 -132.55 L166.7 -134.25 166.75 -134.25 170.0 -136.6 Q171.95 -138.0 172.25 -139.25 L172.1 -140.5 172.1 -141.75 Q172.35 -142.6 172.95 -143.65 173.35 -144.25 173.15 -144.7 L172.9 -145.25 173.0 -145.5 175.35 -144.6 Q179.2 -142.4 180.8 -139.7 181.8 -138.0 182.7 -134.5 184.3 -127.3 179.2 -119.65 170.45 -106.4 143.85 -100.8 135.15 -98.9 123.45 -99.6 110.8 -100.5 101.05 -104.15 95.85 -106.15 94.15 -109.2 93.65 -110.15 92.55 -115.35" fill="url(#gradient0)" fill-rule="evenodd" stroke="none"/>
    </g>
</svg>

回答

不要自己计算新坐标,而是使用上下文的矩阵变换来为您执行此操作,并将与 SVG 中相同的值直接传递给上下文的方法。

我有点懒,所以我不会去重写你所有的值,而是使用一个更简单的形状,但重新引入一个真正的旋转:

const cv = document.createElement('canvas');
cv.width = 200;
cv.height = 200;
const c = cv.getContext('2d');
document.body.appendChild(cv);

// the <g> transform (in order)
c.translate(-50, -500);
// rotate with transform origin
c.translate(150, 700);
c.rotate((Math.PI / 180) * 30)
c.translate(-150, -700);

// the path drawing (relative to the <g>)
c.beginPath();
[[100, 550],[250, 700],[50, 700]]
  .forEach((pt) => c.lineTo(...pt) );
c.closePath();

// same values as in the SVG (cx, cy, 0, cx, cy, rad)
const gradient = c.createRadialGradient(0, 0, 0, 0, 0, 2000);
gradient.addColorStop(0,'rgba(255,0,0,0.34901962)');
gradient.addColorStop(1,'rgba(255,0,0,0)');
c.fillStyle = gradient;

// now we add the gradient's transform to the context's one
c.transform(-0.0664, 0.0141, 0.0063, 0.0288, 150, 600);
// we can finally paint
c.fill('evenodd');
svg {
  border: 1px solid blue;
}
canvas {
  border: 1px solid green;
}
<svg width="200" height="200">
  <radialGradient
      cx="0" cy="0" r="2000"
      gradientTransform="matrix(-0.0664, 0.0141, 0.0063, 0.0288, 150, 600)"
      gradientUnits="userSpaceOnUse" spreadMethod="pad">
    <stop offset="0.0" stop-color="#ff0000" stop-opacity="0.3"/>
    <stop offset="1.0" stop-color="#ff0000" stop-opacity="0.0"/>
  </radialGradient>
  <g transform="translate(-50, -500) rotate(30, 150, 700)">
    <path d="M100 550L250 700L50 700Z" fill="url(#gradient0)"/>
  </g>
</svg>

请注意,我们可以通过使用对象来避免将所有<path>d命令转换为其相应的画布方法,该Path2D对象接受与d属性相同的语法。
但是,使用转换矩阵和 Path2D 有点复杂,尽管并非不可能,如我的这个答案所示。
基本思想是创建 Path2D 对象的副本,通过反转的绘制矩阵进行变换,然后将原始绘制矩阵应用于上下文并绘制该变换后的路径。
DOMMatrix 对象在这里可以提供很大帮助,但说实话,它可能看起来仍然很复杂:

var cv = document.createElement('canvas');
cv.width = 1077;
cv.height = 782;
var c = cv.getContext('2d');
document.body.appendChild(cv);

const path = new Path2D( "M161.0 -139.65 L160.25 -139.45 160.8 -139.85 Q161.55 -140.4 163.6 -140.15 L161.0 -139.65 M92.55 -115.35 Q91.1 -122.1 97.5 -128.9 107.45 -139.25 134.7 -146.75 L142.55 -148.35 Q147.4 -149.25 152.3 -148.3 L160.45 -147.3 161.4 -147.25 159.25 -145.4 Q152.35 -139.0 152.35 -135.5 L152.4 -134.75 152.4 -134.6 151.85 -132.55 151.6 -131.55 151.65 -131.45 Q152.45 -130.95 153.3 -131.0 L153.55 -130.1 Q153.55 -129.7 153.7 -129.45 154.0 -128.9 155.05 -128.9 158.8 -128.9 162.2 -130.4 164.8 -131.5 165.55 -132.55 L166.7 -134.25 166.75 -134.25 170.0 -136.6 Q171.95 -138.0 172.25 -139.25 L172.1 -140.5 172.1 -141.75 Q172.35 -142.6 172.95 -143.65 173.35 -144.25 173.15 -144.7 L172.9 -145.25 173.0 -145.5 175.35 -144.6 Q179.2 -142.4 180.8 -139.7 181.8 -138.0 182.7 -134.5 184.3 -127.3 179.2 -119.65 170.45 -106.4 143.85 -100.8 135.15 -98.9 123.45 -99.6 110.8 -100.5 101.05 -104.15 95.85 -106.15 94.15 -109.2 93.65 -110.15 92.55 -115.35" );

const gradient = c.createRadialGradient( 0, 0, 0, 0, 0, 819.2 );
gradient.addColorStop(0,'rgba(255,0,0,0.34901962)');
gradient.addColorStop(1,'rgba(255,0,0,0)');

// the gradient matrix, inversed,
// to be used when generating our final Path2D object
const grad_mat = new DOMMatrix("matrix(-0.0664, 0.0141, 0.0063, 0.0288, 137.9, -123.45)").inverse();
const transformed_path = new Path2D();
transformed_path.addPath( path, grad_mat );

// the context needs to have the inverted transform
const context_mat = new DOMMatrix();
// we need to add the <g>'s transformation
context_mat.translateSelf(184.85, 404.55);
// and the gradient's
context_mat.multiplySelf( grad_mat.inverse() );

c.setTransform( context_mat );
c.fillStyle = gradient;
c.fill(transformed_path, 'evenodd');

  • 你真的读过我在回答中写的两句话吗?我使用不同的形状 **only** 因为我懒得重写原始文件中的所有命令。你只需要自己做!所以你在我的回答中完全替换了`[[100, 550],[250, 700],[50, 700]].forEach((pt) =&gt; c.lineTo(...pt));` long `c.moveTo(161.0 -139.65);c.lineTo(160.25 -139.45)...` 命令。而且Path2D是一个稳定的技术,不知道为什么在MDN中还被标记为实验性的。这些规范在所有(但已失效的 IE)浏览器中都得到了很好的标准化。
  • 最后一次,制作最小的复制品是为了使答案的要点更加清晰,不会让未来的读者(我为他们编写)陷入无休止的不相关代码行中。懒惰是优秀程序员的特质之一。

以上是是否可以使用当前的MDN画布2DAPI函数在userSpaceOnUse坐标中使用gradientTransforms渲染SVG径向渐变?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>