2

How can I make the white rectangle continually and smoothly follow the cursor position using the move function with a tween?

What I've got so far doesn't work properly — the rectangle position only updates sporadically!

https://jsfiddle.net/jamesgreig/gaxrteoc/73/

var width = 1200
var height = 800
var draw = SVG('drawing').size(width, height)

var building = draw.rect(1200, 800).fill('https://www.dropbox.com/s/pidx5gd1cmt3kyx/mcw_elevation_small.jpg?raw=1')

var rect1 = draw.rect(300,80).fill('#FFF')
rect1.center(width/2, height/2)

var mask2 = draw.mask().add(rect1)
building.maskWith(mask2)

///////

var xPos = 300;
var yPos = 600;

onmousemove = function(e){
 xPos = e.clientX
 yPos = e.clientY
  console.log(xPos+' , '+yPos)
}

/////

function update(dt) {
  // move the rectangle towards the cursor 
  rect1.animate(300).move(xPos - 300, yPos - 80)
}

///////

var lastTime, animFrame;

function callback(ms) {
  // we get passed a timestamp in milliseconds
  // we use it to determine how much time has passed since the last call

  if (lastTime) {
    update((ms-lastTime)/1000) // call update and pass delta time in seconds
  }

  lastTime = ms
  animFrame = requestAnimationFrame(callback)
}

callback()
James Greig
  • 1,683
  • 1
  • 11
  • 16

1 Answers1

1

TL;DR

Don't use animations at all (solution 2) or use the new svg.js v3 controllers (solution 3)

When you call animate() on an element multiple times, the animation gets chained. You call animate with every requestAnimationFrame which means you chain a new animation ever 16ms. That ofc looks very off because all animations are played after each other.

There are multiple solutions to your problem:

Maybe the simplest but maybe not what you want (even if you think so)

svg.js v2

  • stop the animation before you call animate. In your case you need stop(finishAnimation=false, clearQueue=true). The next call to animate then should queue a new animation and immediately play it

svg.js v3

  • in svg.js v3 animate() returns a runner which is scheduled on the elements animation timeline. Before you animate() again, you should remove the runner from the timeline first for performance reasons:

var runner = el.animate()
// later
runner.unschedule()
  • However, this is not required. Then you can schedule a new animation by using the new syntax of animate(duration, delay, when). You want animate(300, 0, 'now'). This will run the new animation right away

Maybe the solution you want but not have been searching for

Don't animate at all. You want to highlight a certain area of a building in your example. Just tie the rectangle to your mouseposition directly:

SVG.on(document, 'mousemove', (e) => {
  // This function converts the mouse coordinates
  // into the space of the draw element. No other math required :)
  const {x, y} = draw.point(e.clientX, e.clientY)
  rect.center(x, y)
})

The coolest and newest solution - but maybe you want consider the second solution anyway

svg.js v3 comes with a new type of animations called declaritive animations. A controller is in charge to always bring the animation where you want it at that moment making mouse following a real breeze.

// Spring() creates a new controller behaving like a physical spring
// You can pass 2 parameters: new SVG.Spring(settleTime, overshoot)
// So if you want jiggling increase the overwhoot (0-20% is useful)
const runner = rect1.animate(new SVG.Spring())

// This part is similar to the second solution
SVG.on(document, 'mousemove', (e) => {
  const {x, y} = draw.point(e.clientX, e.clientY)
  runner.center(x, y)
})

For more information about svg.js v3, twitter is the best source atm because code examples are posted every day until xmas: https://twitter.com/svg_js

Community
  • 1
  • 1
Fuzzyma
  • 7,619
  • 6
  • 28
  • 60