POPSブログ

SVG.jsドラッグ draggable()

384

  Category:  javascript2016/03/13 pops 

SVG.js ドラッグ draggable()について調べてみます。当方独自の方法をも取りますが、一応正常に可動しますからフシギなことです。異常気象が幸いしたのでしょうか...

 

SVG.jsドラッグdraggable()のテスト

 

ご注意、Chrome.Firefox.IE11(一部機能しません)などSVGに対応したブラウザでご覧ください。Chromeを推奨します。

 

SVG.js 使用の基本的な説明


基本的な説明下記ページ参照ください。
【参照】当方の記事: SVG.js を使用してsvgコンテンツを作り表示します

このページとデモページにサンプルと共にJSを記載しています。余り説明などしていませんので、感覚的に違いなど理解ください。
Chrome.Firefox.IE11で確認しながら進めますが、その他のブラウザは未確認です。

 

SVG.jsのDropShadowはエラーで機能しません。カスタムDropShadowは独自のもので、4個をdropshadow-custom.jsで設定している。

【参照】当方の記事: SVG.js DropShadowの自作とBlur


 

SVG.jsドラッグdraggable()


draggable()を実行するには、プラグインsvg.draggable.jsが必要になります。svg.draggable.js


svg.draggable.jsドラッグ設定

ドラッグ設定はスコブル簡単で、ドラッグ範囲の設定と、ドラッグ時の before,start,move,end のイベントにも対応していますので必要に応じて細やかな処理も可能の様です。ホンマかいなソウかいな....


● ドラッグ対象の「SVG.jsインスタンス」つまり、rect要素等に対して.draggable()を実行するだけです。
以下、svg.draggable.jsマニュアルの書式より引用します。


SVG.jsインスタンス.draggable()

例

通常はこれだけでよいので簡単です
var rect=draw.rect(100,100).fill('#000')
.draggable()

-----------------------------------------

範囲を設定する、return値がドラッグ値になる
つまり、x,yを意図的に変更も可能である
rect.draggable(function(x,y){
 //必要な処理も書ける

 return {x:x<1000,y:y<300}
})

-----------------------------------------

オブジェクトで min max 範囲を設定する
rect.draggable({
   minX:10
 , minY:15
 , maxX:200
 , maxY:100
})

-----------------------------------------

Removeドラッグ設定の削除
rect.draggable(false)

● before,start,move,endのイベント追加
beforedrag,dragstart,dragmove,dragendを追加することにより、ドラッグ前後の処理を追加できます。
event.detailにその時点のイベントデータを取得できますので、利用可能です。これ以上の記載およびサンプルなどは有りませんので、自己的に活用ください。(取得値が少し変の様に思うのですが、.....)


// bind
rect.on('dragstart.namespace',function(event){
  // event.detail.event hold the given data explained below
})

// unbind
rect.off('dragstart.namespace')

-----------------------------------------

detail.m transformation matrix to calculate screen coords to coords in the elements userspace
detail.p pageX and pageY transformed into the elements userspace

event.detailの種類
マトリックス形式のオブジェクト取得可能(テストはしていません)
event.detail.m

マウス座標の pageX,pageY 取得可能(スクロール修正はされていない)
event.detail.p

座標
event.detail.p.x
event.detail.p.y

-----------------------------------------

スクロール修正をすれば次の様に成る、サンプル9番参照

x値=event.detail.p.x-(document.body.scrollLeft || document.documentElement.scrollLeft)
y値=event.detail.p.y-(document.body.scrollTop || document.documentElement.scrollTop)

● 重ね順
SVGには z-index の概念は有りませんので、原則SVGそのものでは、ドラッグで重ね順を変更するなどは出来ません(SVG.jsで出来ることが判明、専用のメソッドがありました)。
通常は、描画順に重なります。
下に、「ドラッグで重ね順を変更する」を追加します。2016/03/23

 

ドラッグのサンプル

1番、自由にドラッグできます / 2番、ドラッグ範囲指定 / 3番、重なり順は変更無し


サンプルJS


	//draggable
	//対象エレメントID
	var draw=SVG('draggable-1a').size(160,160)

	var rect=draw.rect(50,50).attr({'fill':'#FFD700'}).center(60,60)
	.draggable()
	.style('cursor','move')

	var txt=draw.text('1 draggable').font({leading:0.8})

	//-------------

	//対象エレメントID
	var draw=SVG('draggable-1b').size(160,160)

	var rectx=draw.rect(120,120).attr({'fill':'none','stroke':'#F00','stroke-width':1,'stroke-dasharray':3}).move(20,20)

	var rect=draw.rect(50,50).attr({'fill':'#FFD700'}).move(20,20)
	.draggable({
		 minX:20
		,minY:20
		,maxX:140
		,maxY:140
	})
	.style('cursor','move')

	var txt=draw.text('2 draggable 範囲指定').font({leading:0.8})

	//-------------

	//対象エレメントID
	var draw=SVG('draggable-1c').size(160,160)

	var rect=draw.rect(50,50).attr({'fill':'#FFD700'}).center(60,60)
	.draggable()
	.attr({'filter':'url(#dropShadow2)'})
	.style('cursor','move')

	var rect2=draw.rect(50,50).attr({'fill':'#DAA520'}).center(100,100)
	.draggable()
	.attr({'filter':'url(#dropShadow2)'})
	.style('cursor','move')

	var txt=draw.text('1 draggable 重ね順変更不可').font({leading:0.8})


4番、画像単独ドラッグ設定 / 5番、画像塗りはNGになる / 6番、GROUP化すればOKです

グループにドラッグ設定して回転した場合、ドラッグが重くなる。 6番でテストできます。(on/offで角度を付ける)


サンプルJS


	//対象エレメントID
	var draw=SVG('draggable-1d').size(160,160)

	var image=draw.image('/main/images/testImage108.jpg').size(100,100).center(80,80)
	.draggable()
	.style('cursor','move')

	var txt=draw.text('4 draggable 画像').font({leading:0.8})

	//-------------

	//対象エレメントID
	var draw=SVG('draggable-1e').size(160,160)

	var image=draw.image('/main/images/testImage108.jpg').size(160,160).center(80,80).scale(100/160)
	var rect=draw.rect(100,100).attr({'fill':image,'stroke':'#000','stroke-width':1}).center(80,80)
	.draggable()
	.style('cursor','move')

	var txt=draw.text('5 draggable 画像塗り NG').font({leading:0.8})

	//-------------

	//対象エレメントID
	var draw=SVG('draggable-1f').size(160,160)

	//グループ化
	var drag_group=draw.group()
	var image=drag_group.image('/main/images/testImage108.jpg').size(160,160).center(80,80).scale(100/160)
	var rect=drag_group.rect(100,100).radius(10).attr({'fill':image,'stroke':'#FFF','stroke-width':2}).center(80,80)
	//グループをdrag
	drag_group.draggable()
	.attr({'filter':'url(#dropShadow2)'})
	.style('cursor','move')

	var txt=draw.text('6 drag GROUP画像塗り OK').font({leading:0.8})

	//ボタン
	var hit_btn=draw.circle(10).attr({'fill':'#FF0000','stroke':'#FFF','stroke-width':2}).center(140,145)
	.attr({'filter':'url(#dropShadow2)'})//陰影
	.style('cursor','pointer')
	//CLICK-ACTION
	hit_btn.click(function() {
		drag_group.toggleClass('angle-on')
		if(drag_group.hasClass('angle-on')) {
			drag_group.rotate(30)
		}else{
			drag_group.rotate(0)
		}
	})
	var hit_text=draw.text('on/off').font({size:10,leading:0.8}).move(100,140)


● 構造と処理次第ではドラッグが正常に動作しないことがありますが、通常、グループ化するとドラッグ出来るようです。
7番、画像マスクGROUP化 / 8番、画像塗り不具合を利用する / 9番、ステージdrag範囲にrectを描画します。再度選択できます。表示の数値はスタート時の x y 位置に成ります。(スクロール修正)

9番、event.detail 取得値が少し変なので、スクロール補正をしたら直った。画像の場合は下に...


9番のdragstartで、ドラッグスタート時のマウス位置をrect原点とする。


//dragstart-bind
bs_rect.on('dragstart.namespace',function(event){
	//マウス座標
	keep_x=mouseX=event.detail.p.x-(document.body.scrollLeft || document.documentElement.scrollLeft)
	keep_y=mouseY=event.detail.p.y-(document.body.scrollTop || document.documentElement.scrollTop)
})

サンプルJS


	//画像マスク
	//対象エレメントID
	var draw=SVG('draggable-1g').size(160,160)

	//dragのためグループ化
	var clip_group=draw.group()
	var mask_image=clip_group.image('/main/images/testImage108.jpg').size(100,100).center(80,80)
	var clip_rect=clip_group.rect(100,100).radius(10).attr({'fill':'#000'}).center(80,80)
	//clip-mask
 	mask_image.clipWith(clip_rect)
	//グループをdrag
	clip_group.draggable()
	.style('cursor','move')
	.attr({'filter':'url(#dropShadow2)'})

	var txt=draw.text('7 drag Clipマスク OK').font({leading:0.8})

	//-------------

	//対象エレメントID
	var draw=SVG('draggable-1h').size(160,160)

	//160x160
	var image=draw.image('/main/images/cloud_160.jpg')
	var move_rect=draw.circle(100).attr({'fill':image}).center(80,80)
	.draggable()
	.style('cursor','move')

	var txt=draw.text('8 画像塗り不具合を利用する').font({leading:0.8})

	//-------------

	//対象エレメントID
	var draw=SVG('draggable-1i').size(160,160)

	//マウス座標変数
	var mouseX,mouseY
	var keep_x=0,keep_y=0

	//グループ
	var group=draw.group()
	group.rect(0,0,160,160)
	//下層
	bs_rect=group.rect(160,160).attr({'fill':'#CCC'}).move(0,0)
	//TEXTdraw層に
	var text_vv=draw.text('9 drag範囲にrectを描画').font({leading:0.8})

	//ドラック範囲のrect
	var dragrect=group.rect(0,0).attr({'fill':'#F09','stroke':'#000','stroke-width':1})
	.draggable()

	bs_rect.draggable(function(x,y){

		text_vv.text(''+ keep_x + ' ' + keep_y)

		bs_rect.move(0,0)
		dragrect.attr({width:x,height:y}).move(keep_x,keep_y)

		return {x:0,y:0}
	})
	//dragstart-bind
	bs_rect.on('dragstart.namespace',function(event){
		//マウス座標
		keep_x=mouseX=event.detail.p.x-(document.body.scrollLeft || document.documentElement.scrollLeft)
		keep_y=mouseY=event.detail.p.y-(document.body.scrollTop || document.documentElement.scrollTop)
	})


● 画像drag範囲にrectを描画

マウス座標の取得は clientX clientY getBoundingClientRect() を求めて計算してみた。同様のサンプルは他にあったがURLのメモがなくなり判らん。サンプルは簡単ですが、実際の応用は大変と思います。

ステージ(画像)、drag範囲にrectを描画します。rectは動かせます。500x200


マウスのmoveリスナーでマウス位置を取得して、dragstart時にrect原点の x y に利用しています。
event.detailは利用していません。


//マウスのmoveリスナー設置
var sum_canvas=document.getElementById("draggable-sum")
sum_canvas.onmousemove=sum_mouseMoveHandler

//マウスHandler
function sum_mouseMoveHandler(e) {
  	var rect=e.target.getBoundingClientRect()
  	//マウス座標の更新
	sum_mouseX=e.clientX-rect.left
	sum_mouseY=e.clientY-rect.top
}

---------------------------------------

//dragstart-bind
sum_bs_rect.on('dragstart.namespace',function(event){
	//マウス座標
	sum_keep_x=Math.floor(sum_mouseX)
	sum_keep_y=Math.floor(sum_mouseY)
})

サンプルJS


	//500x200
	//対象エレメントID
	var draw=SVG('draggable-sum').size(500,200)

	//マウス座標変数2
	var sum_mouseX,sum_mouseY
	var sum_keep_x=0,sum_keep_y=0

	//マウスのmoveリスナー設置
	var sum_canvas=document.getElementById("draggable-sum")
	sum_canvas.onmousemove=sum_mouseMoveHandler

	//グループ
	var sum_group=draw.group()
	sum_group.rect(0,0,160,160)
	sum_image=sum_group.image('/main/images/flower01.jpg').move(0,0)
	//下層
	sum_bs_rect=sum_group.rect(500,200).attr({'fill':sum_image}).move(0,0)
	.style('cursor','crosshair')
	//TEXTdraw層に
	var sum_text_vv=draw.text('5 draggable').font({leading:0.8}).move(350,0)

	//ドラック範囲のrect
	var sum_dragrect=sum_group.rect(0,0).attr({'fill':'rgba(255,0,0,0.4)','stroke':'#000','stroke-width':1,'stroke-dasharray':3})
	.draggable()
	.style('cursor','move')

	sum_bs_rect.draggable(function(x,y){

		x=Math.round(x)
		y=Math.round(y)

		sum_text_vv.text('x ' + sum_keep_x + ' y ' + sum_keep_y + ' w ' + x + ' h '+ y)

		sum_bs_rect.move(0,0)
		sum_dragrect.attr({width:x,height:y}).move(sum_keep_x,sum_keep_y)

		return {x:0,y:0}
	})
	//dragstart-bind
	sum_bs_rect.on('dragstart.namespace',function(event){
		//マウス座標
		sum_keep_x=Math.floor(sum_mouseX)
		sum_keep_y=Math.floor(sum_mouseY)
	})

	//マウスHandler
	function sum_mouseMoveHandler(e) {
  		var rect=e.target.getBoundingClientRect()
  		//マウス座標の更新
		sum_mouseX=e.clientX-rect.left
		sum_mouseY=e.clientY-rect.top
	}


●ドラッグで重ね順を変更する

ドラッグで重ね順を変更は出来ないものと思っていましたが、勘違いでした。サンプル10番の様にすればドラッグしたものが1番上に来ます。とりあえずサンプルを追加しておきますが、詳しい調べ等は後日に述べます。
マニュアル、Arranging elements を参照ください。jQueryの概念があるので(概念が強い、同じような機能と思ってしまう)、テスト前から「重ね順変更」などのメソッドとは思わなかった。(jQueryの概念とは振る舞いが違いますので注意ください)


処理は簡単、共に front() を与えただけです。back()なら一番奥に移動します。

通常のdragの場合


.draggable(function(x,y){
 	//一番前に移動
	this.front()
 	return {x:x,y:y}
})

dragstart追加の場合。11番参照


//dragstart-bind
.on('dragstart.namespace',function(event){
	//一番前に移動
	this.front()
})

11番、dragstartでも処理出来る。角度を与えるとドラッグ範囲が違ってくるので、切れたりの不都合が出る。11番が顕著である。12番、角度を与えないと正常である
まだ完全に修正できる段階では無いので、誰か考えてください。よろしく

// 角度設定での「切れ」を防ぐ方法はアーカイブ386 SVG.js children() の「ドラッグで重ね順の変更とget()」をご覧ください。2016/03/28追加


10番、ドラッグで一番手前に移動 / 11番、dragstart処理 / 12番、角度を与えていない


サンプルJS


	//重ね順変更1
	//対象エレメントID
	var draw=SVG('draggable-1j').size(160,160)

	var spikes,a,point_x,point_y,radius_v,angle12

	spikes=8,a=360/spikes,point_x=80,point_y=80,radius_v=5,
	angle12=-90 //12時補正

	var deep_group=draw.group()
	for (i=0; i < spikes; i++){

		//虹色hsl直接
		var angle_no=Math.floor(i/spikes*360)//0-360
		var color='hsl(' + angle_no + ',100%,50%)'

		var x=point_x+radius_v*Math.cos((i*a+angle12)*Math.PI/180)
		var y=point_y+radius_v*Math.sin((i*a+angle12)*Math.PI/180)
		//rect
		var rect=deep_group.rect(20,20).attr({'fill':color,'stroke':'#FFF','stroke-width':2}).center(x,y).rotate(i*a)
		.attr({'filter':'url(#dropShadow2)'})//陰影
		.draggable(function(x,y){
 			//一番前に移動
			this.front()
 			return {x:x,y:y}
		})
		.style('cursor','move')

	}
	var text=draw.text('10、ドラッグ重ね順変更1').font({leading:0.8})

	//-------------

	//重ね順変更2
	//対象エレメントID
	var draw=SVG('draggable-1k').size(160,160)

	var spikes,a,point_x,point_y,radius_v,angle12

	spikes=8,a=360/spikes,point_x=80,point_y=80,radius_v=50
	angle12=-90 //12時補正

	var deep_group3=draw.group()
	for (i=0; i < spikes; i++){

		//虹色hsl直接
		var angle_no=Math.floor(i/spikes*360)//0-360
		var color='hsl(' + angle_no + ',100%,50%)'

		var x=point_x+radius_v*Math.cos((i*a+angle12)*Math.PI/180)
		var y=point_y+radius_v*Math.sin((i*a+angle12)*Math.PI/180)
		//rect
		var rect=deep_group3.rect(20,20).attr({'fill':color,'stroke':'#FFF','stroke-width':2}).center(x,y).rotate(i*a)
		.attr({'filter':'url(#dropShadow2)'})//陰影
		.draggable()
		.style('cursor','move')
		//dragstart-bind
		.on('dragstart.namespace',function(event){
			//一番前に移動
			this.front()
		})

	}

	var text=draw.text('11、ドラッグ重ね順変更2').font({leading:0.8})

	//-------------

	//重ね順変更3
	//対象エレメントID
	var draw=SVG('draggable-1l').size(160,160)

	var spikes,a,point_x,point_y,radius_v,angle12

	spikes=8,a=360/spikes,point_x=80,point_y=80,radius_v=0

	var deep_group4=draw.group()
	for (i=0; i < spikes; i++){

		//虹色hsl直接
		var angle_no=Math.floor(i/spikes*360)//0-360
		var color='hsl(' + angle_no + ',100%,50%)'

		var x=point_x+radius_v*Math.cos((i*a+angle12)*Math.PI/180)
		var y=point_y+radius_v*Math.sin((i*a+angle12)*Math.PI/180)
		//rect
		var rect=deep_group4.rect(20,20).attr({'fill':color,'stroke':'#FFF','stroke-width':2}).center(x+i*5,y)
		.attr({'filter':'url(#dropShadow2)'})//陰影
		.draggable()
		.style('cursor','move')
		//dragstart-bind
		.on('dragstart.namespace',function(event){
			//一番前に移動
			this.front()
		})

	}

	var text=draw.text('12、ドラッグ重ね順変更3').font({leading:0.8})


● Sliderを作ってみた、
通常作って使用する方はこの世に存在はしないでしょう。AS3、Canvasでも作る人がいないので同じ事と思う。

1番、Slider-1 使える、/ 2番、Slider-2 派生品、/3番、ダイアルSlider、中央(本当はどこでも)クリックで0になる。品質わるし。
回転する上のrectにもドラッグ設定がなされています。


Slider1.2は左上がグループの原点で作り易い。ダイアルSliderは円の中心がグループの原点で、パーツをシンボルで一括描画している。分解すると複雑な計算が必要になるので簡略化しています。説明は省略、下記JS参照ください。

グループの範囲を設定している理由で、陰影は原則付けません(切れる)。グループの範囲を大きくすれば付けることが可能、ダイアルは別の場所に陰影を付けています。


サンプルJS


	//Slider-1
	//対象エレメントID
	var draw=SVG('draggable-2a').size(160,160)

	var group_drag1=draw.group()
	var rect=group_drag1.rect(60,60).fill('#F09').stroke({width:3,color:'#0F9'}).center(80,80)

	var rect2=group_drag1.rect(50,50).fill('#FFF').center(80,80)
	//フォント文字
	group_drag1.text('DRAG')
	.font({
		family:'Arial'
		,size:12
		,anchor:'middle'
		,leading:1
	})
	.move(80,75).attr({'filter':'url(#dropShadow3)'})

	//drag
	group_drag1.style('cursor','move')
	.draggable()

	//groupの大きさを規定して作る/上のグループを回転させてみる
	var group2=draw.group()
	group2.rect(0,0,100,10)
	//100幅の所、角丸のため110幅にしている
	var btn_base=group2.rect(110,8).attr({'fill':'#CCC','stroke':'#888','stroke-width':1}).radius(4).center(50,5)
	var guidline=group2.path('M 0 5 L 100 5').attr({'fill':'none','stroke':'#F00','stroke-width':1})
	var drag_btn=group2.circle(10).attr({'fill':'#F00','stroke':'#000','stroke-width':2}).center(50,5)
	.draggable(function(x,y) {

		//ベースの範囲内に補正/group2を基本とした値になる
		if(x < -5){x=-5}
		if(x > 95){x=95}

		//回転
		group_drag1.rotate(45-x)

  		return {x:x,y:0}
	})
	group2.x(30).y(140)

	var text=draw.text('1 draggable Slider1').font({leading:0.8})

	//-------------

	//対象エレメントID
	var draw=SVG('draggable-2b').size(160,160)

	var group_drag2=draw.group()
	var rect=group_drag2.rect(60,60).fill('#F09').stroke({width:3,color:'#0F9'}).center(80,80)

	//drag
	group_drag2.style('cursor','move')
	.draggable()

	//groupの大きさを規定して作る
	var slide2=draw.group()
	slide2.rect(0,0,100,10)
	//110幅にしている
	var btn_base=slide2.rect(110,8).attr({'fill':'#CCC','stroke':'#888','stroke-width':1}).center(50,5)
	var scalebox=slide2.rect(100,6).attr({'fill':'#FFF'}).center(50,5)
	var scaleline=slide2.rect(100,2).attr({'fill':'#00F'}).move(0,4)
	var drag_btn=slide2.path('M 5 0 L 10 10 L 0 10 Z').attr({'fill':'#F00','stroke':'#000','stroke-width':2}).center(50,5)
	.draggable(function(x,y) {

		//ベースの範囲内に補正/group2を基本とした値になる
		if(x < -5){x=-5}
		if(x > 95){x=95}

		scaleline.width(x)

		//回転
		group_drag2.rotate(45-x)

  		return {x:x,y:0}
	})
	slide2.x(30).y(140)

	var text=draw.text('2 draggable Slider2').font({leading:0.8})

	//-------------

	//対象エレメントID
	var draw=SVG('draggable-2c').size(160,160)

	var group_drag5=draw.group()
	var rect=group_drag5.rect(50,50).fill('#F09').stroke({width:3,color:'#0F9'}).center(80,60)
	.draggable()

	//symbol化/複数登録可能です
	var dialblock=draw.symbol()
	dialblock.circle(40).attr({'fill':'#FFF'})
	dialblock.circle(30).attr({'fill':'#CCC','stroke':'#F00','stroke-width':1}).move(5,5)
	dialblock.circle(8).attr({'fill':'#F00','stroke':'#333','stroke-width':2}).move(31,16)

	var angle_text2=draw.text('0').font({leading:0.8}).move(110,113)
	var angle_v2=0

	var shadow=draw.circle(40).attr({'fill':'#FFF'}).x(60).y(100)
	.attr({'filter':'url(#dropShadow2)'})

	var dialgroup=draw.group()
	dialgroup.rect(-20,-20,40,40)

	//dial
	var dial=dialgroup.use(dialblock).center(0,0)
	.draggable(function(x,y) {
		//角度
		var angle=(180*Math.atan2(x,y)/Math.PI)
		angle_v2=angle*-1

		//TEXT
		var str_r=(Math.round(angle_v2*100)/100).toString()
		angle_text2.text(str_r)
		//ダイアル角度
		dial.rotate(angle_v2)

		//回転
		group_drag5.rotate(angle_v2)

  		return {x:0,y:0}
	})
	dialgroup.x(60).y(100)//.rotate(-90)

	var text=draw.text('3 ダイアルSlider 低品質').font({leading:0.8})


 

サンプルJSの全表示

Chromeは文字化けしますのでエンコード(UTF-8)してご覧ください。

● サンプルJSの全表示1(解説ページ用) svg-sample11.js

使用画像

画像は原則、使用者が用意ください。デモなどで使用した画像は下記に有ります。

使用画像サンプル

 


 

dropshadowは当方自作の dropshadow-custom.js を使用します。
なおこのページはjQuery1.6.4のため dropshadow-custom-ie.js に成ります。

アニメーション書式などは下記の記事を参照ください参照ください。

【参照】当方の記事: SVG.js を使用してsvgコンテンツを作り表示します

【参照】当方の記事: SVG.js DropShadowの自作とBlur


冬は冬眠しています。モウスグ「パチンコ日和」の春です、以上。

 


[ この記事のURL ]


タグ:SVG , javascript

 

ブログ記事一覧



[1]