These two SVGs have linear gradients that are expressed in different coordinate systems, but render the same image. I would like to be able to convert between these coordinate systems. I know how to convert from objectBoundingBox to userSpaceOnUse, but not the other direction.
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="myGradient" x1="80" y1="35" x2="120" y2="115" gradientUnits="userSpaceOnUse">
<stop offset="40%" stop-color="yellow" />
<stop offset="50%" stop-color="black" />
<stop offset="60%" stop-color="red" />
</linearGradient>
</defs>
W<rect x="50" y="50" width="100" height="50" fill="url('#myGradient')" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="myGradient" x1="0%" y1="0%" x2="100%" y2="100%" gradientUnits="objectBoundingBox">
<stop offset="40%" stop-color="yellow" />
<stop offset="50%" stop-color="black" />
<stop offset="60%" stop-color="red" />
</linearGradient>
</defs>
<rect x="50" y="50" width="100" height="50" fill="url('#myGradient')" />
</svg>
In the example below toUserSpaceOnUse
converts the coordinates of an SVG gradient from objectBoundingBox to userSpaceOnUse. How would a function look that does the opposite, converts from userSpaceOnUse to objectBoundingBox coordinates, toObjectBoundingBox
?
draw()
function draw() {
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
function toUserSpaceOnUse(x0, y0, w, h){
let x1 = x0 + w;
let y1 = y0 + h;
let gtransform = 2 / (w / h + h / w);
let xc = (x1 + x0) / 2;
let yc = (y1 + y0) / 2;
let dx = gtransform * (x1 - x0) / 2;
let dy = gtransform * (y1 - y0) / 2;
let rx0 = xc - dy;
let ry0 = yc - dx;
let rx1 = xc + dy;
let ry1 = yc + dx;
let result = [rx0,ry0,rx1,ry1];
return result;
}
function draw(x0, y0, w, h) {
ctx.save();
let c = toUserSpaceOnUse(x0, y0, w, h);
const gradient = ctx.createLinearGradient(c[0], c[1], c[2], c[3]);
gradient.addColorStop(0.4, 'yellow');
gradient.addColorStop(0.5, 'black');
gradient.addColorStop(0.6, 'red');
ctx.fillStyle = gradient;
ctx.fillRect(x0, y0, w, h);
ctx.restore();
}
draw(50, 50, 100, 50);
}
<div>
<p>
With objectBoundingBox coordinates
</p>
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="myGradient" x1="0%" y1="0%" x2="100%" y2="100%" gradientUnits="objectBoundingBox">
<stop offset="40%" stop-color="yellow" />
<stop offset="50%" stop-color="black" />
<stop offset="60%" stop-color="red" />
</linearGradient>
</defs>
<rect x="50" y="50" width="100" height="50" fill="url('#myGradient')" />
</svg>
</div>
<div>
<p>
With userSpaceOnUse coordinates
</p>
<canvas id="canvas" />
</div>