JSFiddle - React, Tailwind, and code Playground
by Gabriel Z
HTML
<!doctype html>
<html>
<head>
<title>Perpective distort of path in SVG</title>
<meta charset="utf-8" />
<script src="http://code.jquery.com/jquery-1.8.2.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.23/jquery-ui.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
</head>
<body>
<svg style="position: absolute; left: 0px; top: 0px; pointer-events: fill; " xmlns="http://www.w3.org/2000/svg" version="1.1" width="2000" height="2000">
<path id="path_1" style="stroke-width: 1px; stroke-opacity: 1; fill-opacity: 0.5; cursor: auto; pointer-events: fill; " fill="#ffba00" stroke="#000000" d="M103.7109375,406L103.7109375,153.4609375L9.375,153.4609375L9.375,119.671875L236.328125,119.671875L236.328125,153.4609375L141.6015625,153.4609375L141.6015625,406L103.7109375,406ZM275.640625,406L275.640625,119.671875L482.671875,119.671875L482.671875,153.4609375L313.53125,153.4609375L313.53125,241.15625L471.9296875,241.15625L471.9296875,274.75L313.53125,274.75L313.53125,372.2109375L489.3125,372.2109375L489.3125,406L275.640625,406ZM512.7578125,406L623.5,256.78125L525.84375,119.671875L570.9609375,119.671875L622.9140625,193.109375Q639.125,215.9609375,645.9609375,228.265625Q655.53125,212.640625,668.6171875,195.6484375L726.234375,119.671875L767.4453125,119.671875L666.859375,254.6328125L775.2578125,406L728.3828125,406L656.3125,303.8515625Q650.2578125,295.0625,643.8125,284.7109375Q634.2421875,300.3359375,630.140625,306.1953125L558.265625,406L512.7578125,406ZM881.7109375,406L881.7109375,153.4609375L787.375,153.4609375L787.375,119.671875L1014.328125,119.671875L1014.328125,153.4609375L919.6015625,153.4609375L919.6015625,406L881.7109375,406Z" ></path>
</svg>
<div id="canvas"></div>
</body>
</html>
CSS
svg {position:absolute;}
JavaScript
var path_source = $("#path_1").attr("d");
// Get bounding box of text path
var bounds = $("#path_1")[0].getBoundingClientRect();
// Make array of coordinates, every array member represent corner of text path
var source = new Array(
{x:bounds.left,y:bounds.top},
{x:bounds.right,y:bounds.top},
{x:bounds.right,y:bounds.bottom},
{x:bounds.left,y:bounds.bottom}
);
// Create SVG with circles and joining lines
var R = Raphael("canvas", 2000, 2000);
var l0 = R.path("M10 10L100 100").attr({stroke:"blue","stroke-width":2});
var l1 = R.path("M10 10L100 110").attr({stroke:"blue","stroke-width":2});
var l2 = R.path("M10 10L100 120").attr({stroke:"blue","stroke-width":2});
var l3 = R.path("M10 10L100 130").attr({stroke:"blue","stroke-width":2});
var dl0 = R.path("M10 10L100 130").attr({stroke:"red","stroke-width":2});
var dl1 = R.path("M10 10L100 130").attr({stroke:"red","stroke-width":2});
var c0 = R.circle(100, 100, 10).attr({fill: "blue",stroke: "none"});
var c1 = R.circle(100, 110, 10).attr({fill: "blue",stroke: "none"});
var c2 = R.circle(100, 120, 10).attr({fill: "blue",stroke: "none"});
var c3 = R.circle(100, 130, 10).attr({fill: "blue",stroke: "none"});
// Corner drag functions (start, move, up)
var start = function () {
// storing original coordinates
this.ox = this.attr("cx");
this.oy = this.attr("cy");
},
move = function (dx, dy) {
// move will be called with dx and dy
this.attr({cx: this.ox + dx, cy: this.oy + dy});
updatelines();
if (isPermissible())
update_text();
},
up = function () {
// restoring state
};
// Function updates lines between corner circles
function updatelines()
{
$(l0.node).attr({d:"M"+c0.attr("cx")+","+c0.attr("cy")+"L"+c1.attr("cx")+","+c1.attr("cy")});
$(l1.node).attr({d:"M"+c1.attr("cx")+","+c1.attr("cy")+"L"+c2.attr("cx")+","+c2.attr("cy")});
$(l2.node).attr({d:"M"+c2.attr("cx")+","+c2.attr("cy")+"L"+c3.attr("cx")+","+c3.attr("cy")});
...