Skip to content

Instantly share code, notes, and snippets.

@1wheel
Last active June 28, 2016 05:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 1wheel/23934d93046d15a031a74be471620036 to your computer and use it in GitHub Desktop.
Save 1wheel/23934d93046d15a031a74be471620036 to your computer and use it in GitHub Desktop.
rotation drag

Drag to move. Shift click and drag left/right to rotate.

Using multiple translates in the transform property didn't work:

transform(d){
  return [
    '  translate(', center,
    ') scale(', d.scale,
    ') rotate(', d.rotate,
    ') translate(', negPos(center), ')'
  ].join('')
}

So I multiplied several affine transformation matrices together to compose the transforms. Math in JS isn't super pretty:

var m = [
  a[0]*b[0] + a[2]*b[1], a[0]*b[2] + a[2]*b[3], a[0]*b[4] + a[2]*b[5] + a[4], 
  a[1]*b[0] + a[3]*b[1], a[1]*b[2] + a[3]*b[3], a[1]*b[4] + a[3]*b[5] + a[5], 
]

return [m[0], m[3], m[1], m[4], m[2], m[5]]

But things could always be worse.

Update: Robert Monfera pointed out that the above transform string was using the wrong variable to translate, the following does work:

transform(d){
  return [
    '  translate(', d.offset,
    ') translate(', d.centroid, ')',
    ') scale(', d.scale,
    ') rotate(', d.rotate*180,
    ') translate(', negPos(d.centroid), ')'
  ].join('')
}
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body{
background: black;
}
path{
fill: green;
fill-opacity: .3;
stroke: white;
stroke-width: .5;
}
</style>
<script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src='script.js'></script>
var width = 960,
height = 500;
var svg = d3.select('html')
.append('svg')
.attr('width', width)
.attr('height', height)
var shapes = d3.range(30).map(function(i){
var rv = {
offset: [Math.random()*width, Math.random()*height],
rotate: Math.random(),
rotateSpeed: (Math.random() - .5)/50,
type: ['cross', 'square', 'triangle-up'][i % 3],
scale: Math.random()*10,
centroid: [0, 0],
}
rv.path = d3.svg.symbol().type(rv.type)()
if (rv.type == 'triangle-up') rv.centroid = [0, 1.754]
return rv
})
var isShift = false
var initalRotate = 0
var initalScale = 1
var drag = d3.behavior.drag()
.on('dragstart', function(d){
isShift = d3.event.sourceEvent.shiftKey
initalRotate = d.rotate
initalScale = d.scale
})
.on('drag', function(d){
var x = d3.event.x
var y = d3.event.y
if (isShift){
d.rotate = initalRotate + x/500
d.scale = initalScale*Math.pow(1.3, (y - d.offset[1])/50)
d.rotateSpeed = 0
} else{
d.offset = [x, y]
}
d3.select(this).attr('transform', transform)
})
.origin(function(d){ return {x: d.offset[0], y: d.offset[1]} })
var shapeSel = svg.selectAll('path')
.data(shapes).enter()
.append('path')
.attr('d', function(d){ return d.path })
.attr('transform', transform)
shapeSel.call(drag)
d3.timer(function(t){
shapeSel
.each(function(d){ d.rotate += d.rotateSpeed })
.attr('transform', transform)
})
function transform(d){
var x = d.centroid[0]
var y = d.centroid[1]
var s = d.scale
var a = d.rotate
var matrix = multAffine(
[1, 0, 0, 1, d.offset[0], d.offset[1]],
[1, 0, 0, 1, x, y],
[s, 0, 0, s, 0, 0],
[Math.cos(a), Math.sin(a), -Math.sin(a), Math.cos(a), 0, 0],
[1, 0, 0, 1, -x, -y]
)
return 'matrix(' + matrix + ')'
return [
'translate(', d.offset, ')',
'translate(', d.centroid, ')',
'scale(', d.scale, ')',
'rotate(', 180 * d.rotate, ')',
'translate(', negPos(d.centroid), ')'
].join(' ')
}
function multAffine(a, b){
if (arguments.length < 2) return a
var m = [
a[0]*b[0] + a[2]*b[1], a[0]*b[2] + a[2]*b[3], a[0]*b[4] + a[2]*b[5] + a[4],
a[1]*b[0] + a[3]*b[1], a[1]*b[2] + a[3]*b[3], a[1]*b[4] + a[3]*b[5] + a[5],
]
m = [m[0], m[3], m[1], m[4], m[2], m[5]]
if (arguments.length < 3) return m
return multAffine.apply(null, [m].concat([].slice.call(arguments).slice(2)))
}
function addPos(a, b){ return [a[0] + b[0], a[1] + b[1]] }
function negPos(a){ return [a[0]*-1, a[1]*-1] }
function rotPos(d, a){ return [Math.cos(a)*d[0], Math.sin(a)*d[1]] }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment