Skip to content

Instantly share code, notes, and snippets.

@rveciana
Last active February 11, 2018 12:38
Show Gist options
  • Save rveciana/7664109 to your computer and use it in GitHub Desktop.
Save rveciana/7664109 to your computer and use it in GitHub Desktop.
Animated arabic kufic calligraphy with D3

Kufic calligraphy has impressed me since long ago. This example is from the walls of the Gudi Khatun Mausoleum in Nakhchivan, Azerbaijan.

Gudi Khatun Mausoleum

The text is animated in the correct order to understand how the words are ordered. The meaning of the text is

There is no God but God, and Muhammad is His prophet. May God bless him.

First, I made the SVG image from the pictures I found. The elements must be lines so they can be animated this way. That's why kufic calligraphy is good for the example, since all the strokes have the same width.

Once the SVG was made, I rotated and scaled, and added to the HTML. Every path was assigned an id of the form id="p14", where the number has to go in the order we want to draw the strokes.

The function drawStroke selects the stroke and changes the stroke-dashoffset as shown in this example.

<!DOCTYPE html>
<html>
<head>
<script src="http://d3js.org/d3.v3.min.js"></script>
<meta charset=utf-8 />
<title>Test Path</title>
</head>
<body>
<svg width="600px" height="600px" >
<g transform="scale(0.5) translate(450,-230) rotate(45) ">
<path
style="fill:none;stroke:#000000;stroke-width:20px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 619.20254,525.91121 0,105.91999 -22.04281,0 0,42.36799 -22.42473,5e-4 0,45.51697 44.46754,-5e-4 0,-45.51697 -22.04281,0 0,-42.36799 -22.42473,5e-4 2.3e-4,-105.91999"
id="p0"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:20px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 530.26791,525.91121 -0.2147,193.80495 -44.25261,0"
id="p1"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:20px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 485.8006,525.91121 0,193.80495 -91.60648,0 0,-43.79935 47.2346,0 0,43.79935"
id="p2"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:20px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 351.53986,525.33527 0,206.38139"
id="p3"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:20px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 306.21385,480.41093 0,150.84603 -22.04281,0 0,42.94223 -22.42473,5e-4 0,45.51697 44.46754,-5e-4 0,-45.51697 -22.04281,0 0,-42.94223 -22.42473,0 2.3e-4,-150.84553"
id="p4"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:20px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 216.99284,436.30862 0,194.94834"
id="p5"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:20px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 218.13793,719.71666 -92.46529,0 4.5e-4,-45.51748"
id="p6"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:20px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 218.71091,674.19918 -93.03782,0 -4.5e-4,-89.03003 48.88336,0 -3.2e-4,46.08803 -48.88336,0"
id="p7"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:20px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 125.81561,496.42537 48.74039,4.9e-4 0,45.42857 -48.74039,-4.9e-4 0,-90.51614"
id="p8"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:20px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 174.91112,392.223 0,59.25794 -49.09551,-0.14314 0,-134.16531 49.09551,0 0,47.85484 -49.09551,0 0,-150.19015 0,58.20198 49.09551,0 0,-58.20198"
id="p9"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:20px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 216.99284,334.37518 0,-106.126 148.63316,0"
id="p10"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:20px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 263.36862,334.11014 -0.57229,-61.07096 45.2307,0 -2.5e-4,61.07096 2.5e-4,-61.07096 42.368,0 0.57229,61.07096 -0.57229,-61.07096 93.89664,0 -2.5e-4,50.47896 -46.66205,0 0,-93.89664 58.66205,0"
id="p11"
inkscape:connector-curvature="0"/>
<path
style="fill:none;stroke:#000000;stroke-width:20px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 484.98708,333.82387 -1.6e-4,-104.20269 58.92638,0"
id="p12"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:20px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 529.045,378.08185 0,-117.94334"
id="p13"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:20px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 558.51324,229.90778 60.68976,3e-4 0,45.51696"
id="p14"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:20px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 558.51324,275.42474 60.68976,3e-4 0,88.17124 -45.52132,-3e-4 0,-43.22681 45.52132,3e-4"
id="p15"
inkscape:connector-curvature="0"/>
<path
style="fill:none;stroke:#000000;stroke-width:20px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 528.45487,454.05745 -0.57254,-48.37975 91.32067,0 0,48.37975 -134.50227,2.4e-4 0,-61.17951 0.57254,103.52424 146.42973,-2.4e-4"
id="p16"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:20px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 497.15244,365.02718 -113.35724,0"
id="p17"
inkscape:connector-curvature="0"/>
<path
style="fill:none;stroke:#000000;stroke-width:20px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 350.967,346.30834 0,107.74984 -42.94,0"
id="p18"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:20px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 308.027,346.30824 0,107.74994 -49.3296,0 3.8e-4,-89.031 -40.88957,0 0,42.008 40.88957,0"
id="p19"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:20px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 397.629,393.27094 0,62.34648 43.8,0.8093 0,-63.15618 -1.2e-4,103.13149"
id="p20"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:20px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 336.02312,496.40233 105.40576,-3e-4 1.2e-4,43.82807"
id="p21"
inkscape:connector-curvature="0"/>
<path
style="fill:none;stroke:#000000;stroke-width:20px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 381.36612,540.2304 60.06288,-3e-4 0,91.02675 -43.8,6.6e-4 0,-46.088 43.8,-6.6e-4"
id="p22"
inkscape:connector-curvature="0" />
</g>
</svg>
<script>
strokeTime = 800; //miliseconds / 500 px
delay = 0;
for (i=0; i<23; i++)
drawStroke(i);
function drawStroke(strokeNum){
var stroke = d3.select('path#p'+strokeNum);
var strokeLength = stroke.node().getTotalLength();
stroke
.style('stroke-dasharray', function(d) {
//debugger
var l = d3.select(this).node().getTotalLength();
return l + 'px, ' + l + 'px';
})
.style('stroke-dashoffset', function(d) {
return d3.select(this).node().getTotalLength() + 'px';
})
.transition()
.delay(delay)
.duration(strokeLength * strokeTime/500)
.ease("linear")
.style('stroke-dashoffset', '0px');
delay = delay + (strokeLength * strokeTime/500);
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment