日別アーカイブ: 2016-11-05

矢印を動的に描く

Svgとmarker要素

svgSvgはHTML5で導入された図形を描くときに適した機能です。当ブログでも急ピッチで取り上げつつあります。その中に矢印を描くmarker要素があります。

線の先端に矢印を付けたいときに使います。時々刻々変化する円弧の先端に矢印を自動的に付加する機能について語ります。

このたび、極座標の回転角をmarker要素にて円弧として描きました。矢印が期待した向きにならず、その制御に手間取ったので、次の機会に困らないように記録に残します。末尾にソースコードを追加しました。

marker-1

誤動作の一例

線の先端に矢印を付ける場合、始点と終点が変化したときは矢印の向きが自動的に追従してもらいたいものです。例えば、0度から360度まで変化する図形を描くとき、最初と最後で期待した動作にならないことがしばしばです。

始値を-0.005に、終値を360.005のようにすれば思い通りの矢印が描けることが多いです。ただし、その媒介変数を他のところで参照することがあり、小さめ、大きめの始値と終値では問題が起きることがあります。

arrow

矢印の向きの実用的な設定法

始値と終値を額面通りに設定し、上図の②と⑤のように表示された場合、矢印描画に使う値だけを設定しなおすことが実用的な解決策と判断しました。末尾に示した23行、24行のコードです。

以下に動作例とソースコードを示します。ここまで到達するにかなりの時間を要しました。参考になればありがたいことです。

動作例

再ロードボタンのクリックで動作します。

ソースコード

<html>
<head>
<script type="text/javascript" charset="Shift_JIS">
const ZX=254;				//中心点x
const ZY=254;				//中心点y
const SIZ0=26;				//半径
const COMT="M 280 254 A 26 26 0 ";
var th=0.0;
var vx=vy=0;
function start(){
	setTimeout('rose1()', 1);
}
function rose1(){
	calculate();
	th += 0.01;
	if(th <= 2.0000000000000013)	setTimeout('rose1()', 10);
}
function calculate(){
	var f1 = (th < 1.0) ? 0 : 1;
	var t = th * Math.PI;									//theta
	var vx = ZX + Math.cos(t) * SIZ0;
	var vy = ZY - Math.sin(t) * SIZ0;
	if(th==0) vy = 253.98;									//矢印の向きを修正
	if(th>=2.00) vy = 254.01;							    //矢印の向きを修正
	//alert("P0");											//初期状態を見るために停止
	var cmd=COMT + f1 + " 0 " + vx + " " + vy;				//COMT="M 286 254 A 32 32 0 "
	cm1.setAttribute("d", cmd);								//曲線
	console.log("TH="+th+" CMD="+cmd);
	//alert("P1");											//1回ごとの描画を確認に停止
}
</script>
<svg width="500px" height="500px" version="1.1" xmlns="http://www.w3.org/2000/svg">
<marker id="mk1" viewBox="0 -5 10 10" refX="5" refY="0" orient="auto" markerWidth="10" markerHeight="10">
  <polygon points="10,0 0,5 0,-5"/>
</marker>
<path id="cm1" d="M 280 253.99 A 26 26 0 1 0 280 254" fill="none" stroke="chocolate" marker-end="url(#mk1)"/>
<path id="xaxis" d="M 210 254 L 298 254" stroke="blue" fill="none" stroke-width="1"/>
<path id="yaxis" d="M 254 210 L 254 298" stroke="blue" fill="none" stroke-width="1"/>
</svg>
</head>
<body onLoad="start()"></body>
</html>