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")});
   ...