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

如何解决是否可以使用当前的 MDN 画布 2D API 函数在 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" id="gradient0" 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>

我试图在这里使用 Canvas 2D API 重现:

var cv = document.createElement('canvas');
cv.width = 1077.15;
cv.height = 781.8;
var c = cv.getContext('2d');
document.body.appendChild(cv);
c.translate(600,300);
c.rotate(0);
c.translate(-600,-300);
// The rotation above was set to 0 to exclude it from implementation until this issue is resolved... though if this is not the correct way to rotate using the API,please let me kNow...
c.translate(80,60);// I'm not sure where I missed out on the math to calculate this offset... I included it to position the path approximately where it should be
c.beginPath();
c.moveto(256.863018149747,203.30007674597084);
c.lineto(256.3059926658311,203.45356868764392);
c.lineto(256.7144780207028,203.14658480429782);
c.quadraticCurveto(257.2715035046187,202.72448196469685,258.79403982732214,202.91634689178818);
c.lineto(256.863018149747,203.30007674597084);
c.moveto(206.0251589843569,221.94934765924793);
c.quadraticCurveto(204.94824304878617,216.7689946277821,209.70152717820176,211.55026861089792);
c.quadraticCurveto(217.09139859815252,203.60706062931698,237.32999118042983,197.85111281657714);
c.lineto(243.16019124541617,196.6231772831927);
c.quadraticCurveto(246.76228937473888,195.93246354566386,250.40152253632272,196.6615502686109);
c.lineto(256.4545327948754,197.4290099769762);
c.lineto(257.1600984078355,197.46738296239448);
c.lineto(255.56329202060994,198.8871834228703);
c.quadraticCurveto(250.43865756858378,203.7989255564083,250.43865756858378,206.4850345356869);
c.lineto(250.4757926008448,207.06062931696087);
c.lineto(250.4757926008448,207.1757482732157);
c.lineto(250.06730724597318,208.74904067536454);
c.lineto(249.8816320846679,209.51650038372986);
c.lineto(249.91876711692893,209.59324635456642);
c.quadraticCurveto(250.51292763310587,209.97697620874908,251.1442231815439,209.9386032233308);
c.lineto(251.3298983428492,210.6293169608596);
c.quadraticCurveto(251.3298983428492,210.9363008442057,251.44130343963235,211.12816577129703);
c.quadraticCurveto(251.66411363319872,211.55026861089792,252.44394931068098,211.55026861089792);
c.quadraticCurveto(255.22907673026043,257.75425892401245,210.39907904834996);
c.quadraticCurveto(259.6852806015875,209.55487336914814,260.2423060855034,208.74904067536454);
c.lineto(261.0964118275078,207.44435917114353);
c.lineto(261.13354685976884,207.44435917114353);
c.lineto(263.54732395673767,205.6408288564851);
c.quadraticCurveto(264.995590214919,204.56638526477363,265.21840040848537,203.60706062931698);
c.lineto(265.1069953117022,202.64773599386035);
c.lineto(265.1069953117022,201.6884113584037);
c.quadraticCurveto(265.2926704730075,201.0360706062932,265.7382908601402,200.23023791250958);
c.quadraticCurveto(266.0353711182287,199.76976208749042,265.88683098918443,199.42440521872604);
c.lineto(265.7011558278791,199.00230237912513);
c.lineto(265.77542589240124,198.8104374520338);
c.lineto(267.52077240867106,199.5011511895626);
c.quadraticCurveto(270.3801698927726,201.18956254796623,271.5684909251265,203.2617037605526);
c.quadraticCurveto(272.3111915703477,272.9796221510467,207.2524942440522);
c.quadraticCurveto(274.16794318340067,212.77820414428243,270.3801698927726,218.64927091327704);
c.quadraticCurveto(263.88153924708723,228.81811204911742,244.1257020842037,233.11588641596316);
c.quadraticCurveto(237.66420647077936,234.57405986185725,228.9746089216915,234.0368380660016);
c.quadraticCurveto(219.57944575964353,233.34612432847277,212.33811446873696,230.54489639293936);
c.quadraticCurveto(208.4760711135868,229.00997697620875,207.21348001671075,226.6692248656946);
c.quadraticCurveto(206.84212969410015,225.9401381427475,206.0251589843569,221.94934765924793);
c.closePath();
var gradient=c.createradialGradient(206.0251589843569,196.6231772831927,819.2*66.95446316668983);// ObvIoUsly this cannot be the correct conversion from the bounding Box (x:206.0251589843569,y:196.6231772831927,w:66.95446316668983,h:37.413660782808904) to user system coordinates..
gradient.addColorStop(0,'rgba(255,0.34901962)');
gradient.addColorStop(1,0)');
c.transform(-0.0664,-123.45);
c.fillStyle=gradient;
c.fill('evenodd');

如上面的评论所述,我无法获得正确的渐变中心,我相信我一定误解了 SVG 的实施指南。我能否解释一下如何使用 userSpaceOnUse 来实现这一点,或者我没有正确做些什么,因为使用 objectBoundingBox 效果很好?

解决方法

不要自己计算新坐标,而是使用上下文的矩阵变换来为您执行此操作,并将与 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,cx,rad)
const gradient = c.createRadialGradient(0,2000);
gradient.addColorStop(0,'rgba(255,0.34901962)');
gradient.addColorStop(1,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 id="gradient0"
      cx="0" cy="0" r="2000"
      gradientTransform="matrix(-0.0664,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,700)">
    <path d="M100 550L250 700L50 700Z" fill="url(#gradient0)"/>
  </g>
</svg>

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

d

,

<!DOCTYPE html>
<head><title>Test</title></head><body>
<script>
var cv = document.createElement('canvas');
cv.width = 1077.15;
cv.height = 781.8;
document.body.appendChild(cv);
var c = cv.getContext('2d');
c.globalAlpha=1;
c.setTransform(1,1,0);
c.transform(1,-124.98259295362763,-9.989767203888464);
c.transform(1,309.8068514134522,414.12579943719624);
c.translate(600,300);
c.rotate(0);
c.translate(-600,-300);
c.translate(600,-300);
c.beginPath();
c.moveTo(160.97757972427237,-139.5070990023024);
c.lineTo(160.2276841665506,-139.3073036582246);
c.lineTo(160.7776075755466,-139.70689434638015);
c.quadraticCurveTo(161.52750313326834,-140.25633154259404,163.57721765770782,-140.00658736249682);
c.lineTo(160.97757972427237,-139.5070990023024);
c.moveTo(92.5371118228659,-115.23196469685342);
c.quadraticCurveTo(91.0873137446038,-121.97505755947813,97.48642250382954,-128.7680992581223);
c.quadraticCurveTo(107.43503690293831,-139.10750831414686,134.68124216682912,-146.5998337170632);
c.lineTo(142.53014900431697,-148.19819646968534);
c.quadraticCurveTo(147.3794736109177,-149.0972755180353,152.2787912546999,-148.14824763366593);
c.lineTo(160.4276563152764,-147.14927091327706);
c.lineTo(161.37752402172399,-147.09932207725763);
c.lineTo(159.2278234229216,-145.25121514453826);
c.quadraticCurveTo(152.32878429188133,-138.85776413404963,152.32878429188133,-135.36134561268867);
c.lineTo(152.3787773290628,-134.61211307239705);
c.lineTo(152.3787773290628,-134.4622665643387);
c.lineTo(151.82885392006682,-132.4143642875416);
c.lineTo(151.5788887341596,-131.41538756715275);
c.lineTo(151.62888177134104,-131.31548989511384);
c.quadraticCurveTo(152.42877036624424,-130.81600153491942,153.27865199832894,-130.86595037093886);
c.lineTo(153.5286171842362,-129.9668713225889);
c.quadraticCurveTo(153.5286171842362,-129.56728063443336,153.67859629578052,-129.31753645433614);
c.quadraticCurveTo(153.97855451886923,-128.7680992581223,155.0284082996797,-128.7680992581223);
c.quadraticCurveTo(158.77788608828854,162.1774126166272,-130.26656433870556);
c.quadraticCurveTo(164.77705055006268,-131.36543873113328,165.52694610778443,-132.4143642875416);
c.lineTo(166.67678596295778,-134.11262471220263);
c.lineTo(166.72677900013926,-134.11262471220263);
c.lineTo(169.97632641693357,-136.4602200051164);
c.quadraticCurveTo(171.92605486701015,-137.8587874136608,172.22601309009886,-139.10750831414686);
c.lineTo(172.0760339785545,-140.35622921463292);
c.lineTo(172.0760339785545,-141.60495011511895);
c.quadraticCurveTo(172.32599916446176,-142.45408032744947,172.92591561063918,-143.50300588385778);
c.quadraticCurveTo(173.3258599080908,-144.10239191609108,173.12588775936499,-144.55193144026606);
c.lineTo(172.87592257345773,-145.10136863647992);
c.lineTo(172.97590864782063,-145.35111281657714);
c.lineTo(175.32558139534882,-144.45203376822718);
c.quadraticCurveTo(179.17504525832055,-142.25428498337172,180.774822448127,-139.5570478383218);
c.quadraticCurveTo(181.77468319175603,182.67455786102212,-134.36236889229983);
c.quadraticCurveTo(184.27433505082857,-127.16973650550014,179.17504525832055,-119.52756459452547);
c.quadraticCurveTo(170.4262637515666,-106.29112304937325,143.82996797103468,-100.6968534151957);
c.quadraticCurveTo(135.13117950146219,-98.7987976464569,123.43280880100265,-99.4980813507291);
c.quadraticCurveTo(110.78457039409552,-100.39716039907906,101.03592814371257,-104.04342542849835);
c.quadraticCurveTo(95.83665227684166,-106.04137886927604,94.13688901267233,-109.08825786646202);
c.quadraticCurveTo(93.63695864085783,-110.03728575083143,92.5371118228659,-115.23196469685342);
c.closePath();
var gradient=c.createRadialGradient(0,819.2);
gradient.addColorStop(0,0)');
c.fillStyle=gradient;
c.transform(0.999860743629021,0.9989767203888463,0);
c.transform(-0.0664,137.9,-123.45);
c.fill('evenodd');
c.rotate(0);
</script></body><html>

以上是使用 Canvas API 正确呈现的 JS,它将反映此问题中 SVG 的确切实现。我已经为其他人发布了这个解决方案,以防他们遇到同样的问题。它在上面不起作用的原因是因为浏览器会跟踪变换矩阵并且不会简单地乘以和丢弃它(我这样做是为了提高性能)所以简单地将变换直接应用于路径 [尽管很明显] 不会生成正确渲染 [& 在这种情况下,缩放] 梯度所需的正确用户空间矩阵。手动缩放 SVG 的棘手部分实际上是嗅出任何变换并相应地调整它们——这是下一步。当使用 SVG 的 viewBox 属性更改图像的坐标系时,实际上会发生这种情况。通常应按顺序处理变换,并在 SVG 坐标系中进行任何初始调整之后。它可以直接在路径值中完成,但是我犯了一次错误而不是多次执行;特别是在应用梯度的地方——存储原始变换矩阵会有所帮助。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?