Is it possible to morph SVG's paths using Velocity.js? - svg

Is it possible to morph SVG's paths using Velocity.js?
FROM
"M292,129c55.2,0,193,125.8,193,181S365.7,506,310.5,506S136,355.2,136,300S236.8,129,292,129z"
TO
"M230,38c55.2,0,348,57.8,348,113S391.2,569,336,569S55,456.2,55,401S174.8,38,230,38z"
var path = document.querySelectorAll('svg path');
Velocity(path[0], {
tween: 1000
}, {
duration: 6000,
easing: 'easeOutBounce',
progress: function (el, c, r, s, t) {
el[0].setAttribute('d', ??????);
}
});

EDIT: Please note I am the author of the code that enabled this in 2016, so it is the official supported way to do it!
Almost, should be something like this:
var path = document.querySelectorAll('svg path'),
from = "M292,129c55.2,0,193,125.8,193,181S365.7,506,310.5,506S136,355.2,136,300S236.8,129,292,129z",
to = "M230,38c55.2,0,348,57.8,348,113S391.2,569,336,569S55,456.2,55,401S174.8,38,230,38z";
Velocity(path[0], {
tween: [to, from]
}, {
duration: 6000,
easing: 'easeOutBounce',
progress: function(elements, complete, remaining, start, tweenValue) {
elements[0].setAttribute('d', tweenValue);
}
});

Edit: Velocity has some string animation support built in, see Rycochets answer below.
If not you could try doing that yourself, by breaking down the path string into an array.
One way could be to use the path data polyfill (as Chrome has deprecated that feature to give easy access to the array points) at polyfill. Then you could loop similar to below through the path points and interpolate.
You could also try using some regex to split and then build back up, a quick example could be something like the following. It's probably not complete (I haven't really tested the regex, and you may get varying number of elements if there are some characters that I haven't thought of).
$velocity=$("#mypath");
var fromPath = "M292,129c55.2,0,193,125.8,193,181S365.7,506,310.5,506S136,355.2,136,300S236.8,129,292,129z";
var toPath = "M230,38c55.2,0,348,57.8,348,113S391.2,569,336,569S55,456.2,55,401S174.8,38,230,38z";
var re = /(([+-]?[0-9\.]+)|[a-z]|\s+)([eE][-+]?[0-9]+)?/gi
var fromMatch = fromPath.match(re)
var toMatch = toPath.match(re)
$velocity.velocity(
{ opacity: 0.5, tween: [0,1] },
{ progress: function( el, complete, remaining, start, tweenValue) {
var interpPath = '';
for( c=0; c<fromMatch.length; c++) {
if( !isNaN( fromMatch[c]) ) {
interpPath += ( toMatch[c] - fromMatch[c] ) * tweenValue + +fromMatch[c]
} else {
interpPath += toMatch[c]
}
}
el[0].setAttribute('d', interpPath)
} }
)
jsfiddle

Related

phaser.io how to draw over a path

I need to write a game like: https://robowhale.com/html5/drawing-letters/ with phaser.io library. I mean user must follow a path and draw for example letter "A".
basically, need to draw over a path, I checked almost all examples and tutorials, But couldn't find any proper tutorial or algorithm.
any help, link, source code or tutorial can helps me to figure out algorithm and start project.
Usually you will never find the full solution, you will have to merge multiple.
Here ist how I would approach this task (a quick and dirty solution):
Step 1)
Find an Example that solves a part of the problem and work from there
(Based on the example Quadratic Bezier Curve)
Then I :...
I removed the tween
added Mouse Input
split the path in segments
calculate until where the path should be draw
draw path segments
... And slowly adding missing features:
just update path when close enough
just allow move forwards
...
var config = {
type: Phaser.AUTO,
width: 800,
height: 600,
backgroundColor: '#2d2d2d',
parent: 'phaser-example',
scene: {
create: create,
update: update
}
};
var path;
var curve;
var graphics;
var game = new Phaser.Game(config);
var _myMaxPointIndex = 6;
function create() {
graphics = this.add.graphics();
path = { t: 0, vec: new Phaser.Math.Vector2() };
var startPoint = new Phaser.Math.Vector2(100, 500);
var controlPoint1 = new Phaser.Math.Vector2(50, 100);
var endPoint = new Phaser.Math.Vector2(700, 500);
curve = new Phaser.Curves.QuadraticBezier(startPoint, controlPoint1, endPoint);
}
function _myDrawPath(g, points) {
let startPoint = points.shift();
graphics.lineStyle(30, 0x0000ff, 1);
g.beginPath();
g.moveTo(startPoint.x, startPoint.y);
let maxPointsToDraw = _myMaxPointIndex == -1 ? points.length : _myMaxPointIndex + 1;
for (let index = 0; index < maxPointsToDraw; index++) {
const point = points[index];
g.lineTo(point.x, point.y);
}
g.strokePath();
}
function update() {
graphics.clear();
graphics.lineStyle(2, 0x00ff00, 1);
curve.draw(graphics);
// get 20 Point from the Curve (can be more if to jaggy)
let _myPoints = curve.getPoints(20);
_myDrawPath(graphics, _myPoints)
if (this.input.activePointer.isDown) {
// Here I update the Max point that should be draw
let _myMouse = this.input.activePointer.position;
let _myNearestPoint = _myPoints.reduce((p, c, i) => {
let distance = Phaser.Math.Distance.BetweenPoints(_myMouse, c)
if (p.distance == -1 || p.distance > distance) {
p.distance = distance
p.idx = i
}
return p
}, { distance: -1 })
_myMaxPointIndex = _myNearestPoint.idx
}
}
h1 {
font-family:arial
}
#phaser-example{
transform: translate(-20%, -20%) scale(.5);
}
<script src="//cdn.jsdelivr.net/npm/phaser#3.55.2/dist/phaser.min.js"></script>
<h1>Click to calculate path</H1>
<div id="phaser-example"></div>
And with a bit of luck:
While building this solution, I had to "google" for some documentation details, and found this in a Phaser forum, that points to a interesting solution with a working CodePen, with a more complex full working example (Just adding the codepen link if the forum entry gets deleted).

How to keep previous data in Leaflet Realtime

I am trying to use Leaflet realtime plugin (https://github.com/perliedman/leaflet-realtime), they mentioned in the documentation that we can keep previous updates by adding start:false in the constructor.
var map = L.map('map'),
realtime = L.realtime({
url: 'https://wanderdrone.appspot.com/',
crossOrigin: true,
type: 'json'
}, {
interval: 3 * 1000, start:false
}).addTo(map);
Anyone have better idea on how to do that ?
plnkr has a good demo:
http://plnkr.co/edit/NmtcUa?p=preview
If you set start: false, automatic updates will be disabled. This means you will have to call the layer's update method yourself, providing any GeoJSON data you want to add or update; you can also remove added features with the remove method. These methods can be used if you want to use something other than server polling.
You can use the following code that I copied from plnkr while changed a little bit, because L.realtime inherit L.geoJson. And L.geoJson have 'layeradd'.
realtime.on('layeradd', function(e) {
var coordPart = function(v, dirs) {
return dirs.charAt(v >= 0 ? 0 : 1) +
(Math.round(Math.abs(v) * 100) / 100).toString();
},
popupContent = function(fId) {
var feature = e.features[fId],
c = feature.geometry.coordinates;
return 'Wander drone at ' +
coordPart(c[1], 'NS') + ', ' + coordPart(c[0], 'EW');
},
bindFeaturePopup = function(fId) {
realtime.getLayer(fId).bindPopup(popupContent(fId));
},
updateFeaturePopup = function(fId) {
realtime.getLayer(fId).getPopup().setContent(popupContent(fId));
};
map.fitBounds(realtime.getBounds(), {maxZoom: 3});
Object.keys(e.enter).forEach(bindFeaturePopup);
Object.keys(e.update).forEach(updateFeaturePopup);
});

raphael change animation direction

simple by using rapheal i successfully make animation along path , but i can't reverse the animation direction ,,, just how to make it animate to the other direction when clicking the same path .
var paper = Raphael(0,0,1024,768);
var pathOne = paper.path(['M', 15,15 , 100,75]).attr({'stroke-width':18}).data("id",1);
//and this is just the circle
var circle = paper.circle(0, 0, 13).attr({
fill: '#09c', cursor: 'pointer'
});
//make the path as custom attribute so it can ba accessed
function pathPicker(thatPath){
paper.customAttributes.pathFactor = function(distance) {
var point = thatPath.getPointAtLength(distance * thatPath.getTotalLength());
var dx = point.x,
dy = point.y;
return {
transform: ['t', dx, dy]
};
}
}
//initialize for first move
pathPicker(pathOne);
circle.attr({pathFactor: 0}); // Reset
//Asign first path and first move
function firstMove(){
circle.animate({pathFactor: 1}, 1000});
}
pathOne.click(function(){
firstMove();
});
I couldn't get the original to run, so here is something using the main bits that should suit...
There's not a lot to it, get the length of the path, iterate over the length, draw object at the path. It uses the Raphael customAttributes to be able to animate it. I've added a custom toggle to make it easy to switch between them.
These are the key changes..
var len = pathOne.getTotalLength();
paper.customAttributes.along = function (v) {
var point = pathOne.getPointAtLength(v * len);
return {
transform: "t" + [point.x, point.y] + "r" + point.alpha
};
};
circle.attr({ along: 0 });
function animateThere( val ) {
val = +!val; // toggle
pathOne.click( function() { animateThere( val ) } );
circle.animate({ along: val }, 2000 );
};
pathOne.click( function() { animateThere(0) } );
jsfiddle
For completeness, you may want to do some extra checks like only allow the click if the animation has finished or something, as there may be a problem if you quickly click a lot and it buffering up animations.

Set window width in jsDom?

Should be a simple question. How do I set the width in a jsDom object?
jsdom.env({
url:'http://testdatalocation',
scripts: ['http://code.jquery.com/jquery.js'],
done: function(errors, tstWindow) {
console.log(tstWindow.innerWidth);
};
}
});
I can't figure out how to get the "innerWidth" to be anything but 1024
The resizeTo and resizeBy methods are not implemented. You can see that by searching through the code base of jsdom:
$ grep -P 'resize(To|By)' `find . -type f`
./lib/jsdom/browser/index.js: resizeBy: NOT_IMPLEMENTED(null, 'window.resizeBy'),
./lib/jsdom/browser/index.js: resizeTo: NOT_IMPLEMENTED(null, 'window.resizeTo'),
If you just want to set the window size once and for all at initialization time, you could just set the innerWidth value to whatever you want. In a real browser, this is not the right way to do it, but in jsdom it would work.
However, if you have code that depends on resizeTo being present, you can add your own polyfill to the constructor that builds windows:
var jsdom = require("jsdom");
var document = jsdom.env({
html: "<html></html>",
done: function (error, w) {
console.log(w.innerWidth, w.innerHeight);
w.constructor.prototype.resizeTo = function (width, height) {
this.innerWidth = this.outerWidth = width;
this.innerHeight = this.outerHeight = height;
};
w.resizeTo(100, 200);
console.log(w.innerWidth, w.innerHeight);
}
});
This displays:
1024 768
100 200
The code above is for illustration purposes. I've not thought about all the ins and outs of writing a polyfill for resizeTo. resizeBy would be handled similarly but would add deltas to the size of the window.
There isn't currently a formal option or API for doing so.
The values of innerWidth and similar properties are simply set to literal values:
DOMWindow.prototype = createFrom(dom || null, {
// ...
name: 'nodejs',
innerWidth: 1024,
innerHeight: 768,
outerWidth: 1024,
outerHeight: 768,
// ...
});
Beyond test cases and documentation, outerWidth isn't referenced elsewhere else in jsdom, so you could probably assign a new value within the created event, updating outerWidth as well.
The primary use-case for created is to modify the window object (e.g. add new functions on built-in prototypes) before any scripts execute.
created: function (errors, tstWindow) {
tstWindow.outerWidth = tstWindow.innerWidth = 1440;
},
done: function(errors, tstWindow) {
console.log(tstWindow.innerWidth);
}

Is it possible to animate filter in Fabric.js?

Is it possible to animate the images filter in Fabric.js? Such as a "pixelate" filter.
I solved it in the same way like the demo.
Unfortunately filters aren't able to be animated - they need too much processing time.
Here's my Code:
image = ... //Image, where the filter should be applied
var filter = new fabric.Image.filters.RemoveWhite({
threshold: 0,
distance: 140
});
image.filters.push(filter);
image.applyFilters(canvas.renderAll.bind(canvas));
animate(image,1, 400); //Start the Animation
function animate(image,value, stop){
value += ((stop-value)*0.02); //Change the threshold-value
if (image.filters[0]) {
image.filters[0]['threshold'] = value;
console.log(value);
image.applyFilters(canvas.renderAll.bind(canvas)); //Start creating the new image
if(value<stop-100){
setTimeout(function(){act(image,value,stop);},1);
}
}
}
I know the code isn't the most efficient one, but it works. And you can see that Animating filters consumes too much time.
(I tested it with a 1920x1080px image, maybe you can use it with smaller images)
Here is a updated version for the brightness filter
var brightnessValue = 0.9;
var brightnessFilter = new fabric.Image.filters.Brightness({
brightness: brightnessValue
});
fabricImage.filters.push(brightnessFilter);
fabric.util.requestAnimFrame(function brightnessFilterAnimation() {
brightnessValue = brightnessValue - 0.04;
brightnessFilter.brightness = brightnessValue;
fabricImage.applyFilters();
if (brightnessValue > 0) {
fabric.util.requestAnimFrame(brightnessFilterAnimation);
}
});

Resources