I am looking at this example. How this can be done using Raphael for below example ?
Raphael("canvas", function () {
var win = Raphael._g.win,
doc = win.document,
hasTouch = "createTouch" in doc,
M = "M",
L = "L",
d = "d",
COMMA = ",",
// constant for waiting doodle stop
INTERRUPT_TIMEOUT_MS = hasTouch ? 100 : 1,
// offset for better visual accuracy
CURSOR_OFFSET = hasTouch ? 0 : -10,
paper = this,
path = "", // hold doodle path commands
// this element draws the doodle
doodle = paper.path(path).attr({
"stroke": "rgb(255,0,0)"
}),
// this is to capture mouse movements
tracker = paper.rect(0, 0, paper.width, paper.height).attr({
"fill": "rgb(255,255,255)",
"fill-opacity": "0.01"
}),
active = false, // flag to check active doodling
repath = false, // flag to check if a new segment starts
interrupt; // this is to connect jittery touch
tracker.mousedown(function () {
interrupt && (interrupt = clearTimeout(interrupt));
active = true;
repath = true;
});
tracker.mousemove(function (e, x, y) {
// do nothing if doodling is inactive
if (!active) {
return;
}
// Fix for Raphael's touch xy bug
if (hasTouch &&
(e.originalEvent.targetTouches.length === 1)) {
x = e.clientX +
(doc.documentElement.scrollTop || doc.body.scrollTop || 0);
y = e.clientY +
(doc.documentElement.scrollLeft || doc.body.scrollLeft || 0);
e.preventDefault();
}
// Insert move command for a new segment
if (repath) {
path += M + (x + CURSOR_OFFSET) + COMMA +
(y + CURSOR_OFFSET);
repath = false;
}
path += L + (x + CURSOR_OFFSET) + COMMA +
(y + CURSOR_OFFSET); // append line point
// directly access SVG element and set path
doodle.node.setAttribute(d, path);
});
// track window mouse up to ensure mouse up even outside
// paper works.
Raphael.mouseup(function () {
interrupt && (interrupt = clearTimeout(interrupt));
// wait sometime before deactivating doodle
interrupt = setTimeout(function () {
active = false;
}, INTERRUPT_TIMEOUT_MS);
});
Above code is copied from https://stackoverflow.com/a/17781275/1595858
To create a smooth line through several points, use Raphael's Catmull-Rom extension to SVG paths. See: http://raphaeljs.com/reference.html#Paper.path .
// Creating a line:
var line = paper.path("M x0 y0 R x1 y1 x2 y2 x3 y3 x4 y4");
// Adding next point:
line.attr("path", line.attr("path") + " x5 y5");
The easiest way to smoothen the lines in your case (using Raphael) is to use the Raphael._path2curve function.
The place where you are doing doodle.node.setAttribute(d, path); replace it with the following line, thus replacing all line segments with curves.
doodle.node.setAttribute(d, Raphael._path2curve(path));
Note that this will result in degradation of performance. There is a huge scope of improvement of this by realtime converting path to curves instead of converting the entire path every time.
Related
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).
I'm scaling a displacement map on click but would like that map to fade away once it reaches almost full scale. The idea is that the filter should be non-existent after a couple of seconds.
const app = new PIXI.Application({
view: document.querySelector("#canvas"),
width: 512,
height: 512
});
const logo = PIXI.Sprite.fromImage("https://unsplash.it/600");
const displacement = PIXI.Sprite.fromImage("https://images.unsplash.com/photo-1541701494587-cb58502866ab?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&w=1000&q=80");
const filter = new PIXI.filters.DisplacementFilter(displacement);
logo.anchor.set(0.5);
logo.position.set(256);
logo.interactive = true;
displacement.anchor.set(0.5);
displacement.position.set(256);
displacement.scale.set(0.05)
displacement.alpha = 1
app.stage.filterArea = app.screen;
app.stage.filters = [filter];
app.stage.addChild(logo, displacement);
app.ticker.add(function() {
displacement.scale.x += 0.05
displacement.scale.y += 0.05
if (displacement.scale.x > 10) app.ticker.stop()
});
logo.on('mousedown', function() {
displacement.scale.set(0.05)
app.ticker.start()
});
Here's what I have so far:
https://codepen.io/mariojankovic/pen/pojjNae?editors=0111
I've only just started looking at Pixi but I think you want to use the scale property of the displacement filter. This value says how far to shift. Reducing this value to 0 will lessen its effect to none.
https://pixijs.download/dev/docs/PIXI.filters.DisplacementFilter.html
https://pixijs.download/dev/docs/PIXI.filters.DisplacementFilter.html#scale
The way it works is it uses the values of the displacement map to look
up the correct pixels to output. This means it's not technically
moving the original. Instead, it's starting at the output and asking
"which pixel from the original goes here". For example, if a
displacement map pixel has red = 1 and the filter scale is 20, this
filter will output the pixel approximately 20 pixels to the right of
the original.
https://codepen.io/PAEz/pen/BaoREwv
const app = new PIXI.Application({
view: document.querySelector("#canvas"),
width: 512,
height: 512
});
const logo = PIXI.Sprite.fromImage("https://unsplash.it/600");
const displacement = PIXI.Sprite.fromImage(
"https://images.unsplash.com/photo-1541701494587-cb58502866ab?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&w=1000&q=80"
);
const filter = new PIXI.filters.DisplacementFilter(displacement);
logo.anchor.set(0.5);
logo.position.set(256);
logo.interactive = true;
displacement.anchor.set(0.5);
displacement.position.set(256);
displacement.scale.set(0.0);
displacement.alpha = 1;
app.stage.filterArea = app.screen;
app.stage.filters = [filter];
app.stage.addChild(logo, displacement);
const displacementScaleFrom = 0.05;
const displacementScaleTo = 10 ;
const displacementStep = 0.05;
const filterScaleFrom = 20;// the default value for the filter is 20
const filterStep = filterScaleFrom / ((displacementScaleTo-displacementScaleFrom) / displacementStep);
app.ticker.add(function () {
displacement.scale.x += displacementStep;
displacement.scale.y += displacementStep;
filter.scale.x -= filterStep;
filter.scale.y -= filterStep;
if (displacement.scale.x >= displacementScaleTo) {
app.ticker.stop();
filter.scale.x = 0;
filter.scale.y = 0;
}
});
logo.on("mousedown", function () {
displacement.scale.set(displacementScaleFrom);
filter.scale.x = filterScaleFrom;
filter.scale.y = filterScaleFrom;
app.ticker.start();
});
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.
another ThreeJS question:
How can I make a hovered (intersected) object scale smooth to a defined size? My current code is
INTERSECTED.scale.x *= 1.5;
INTERSECTED.scale.y *= 1.5;
but this only works like on/off.
Edit: My Solution (with tweenJS)
While mouseOver I scale up the element to 200% :
function scaleUp(){
new TWEEN.Tween( INTERSECTED.scale ).to( {
x: 2,
y: 2
}, 350 ).easing( TWEEN.Easing.Bounce.EaseOut).start();
}
While mouseOut I scale down the element to 100% :
function scaleDown(){
new TWEEN.Tween( INTERSECTED.scale ).to( {
x: 1,
y: 1
}, 350 ).easing( TWEEN.Easing.Bounce.EaseOut).start();
}
Finally I call the functions in the mouse function.
if (intersects[0].object != INTERSECTED)
scaleUp();
else
scaleDown();
That's all. Very useful for UIs I think.
Using the tween library (found in three.js/examples/js/libs/tween.min.js) you can animate the scale like so:
function setupObjectScaleAnimation( object, source, target, duration, delay, easing )
{
var l_delay = ( delay !== undefined ) ? delay : 0;
var l_easing = ( easing !== undefined ) ? easing : TWEEN.Easing.Linear.None;
new TWEEN.Tween( source )
.to( target, duration )
.delay( l_delay )
.easing( l_easing )
.onUpdate( function() { object.scale.copy( source ); } )
.start();
}
and then call it like so:
setupObjectScaleAnimation( INTERSECTED,
{ x: 1, y: 1, z: 1 }, { x: 2, y: 2, z: 2 },
2000, 500, TWEEN.Easing.Linear.None );
Or if you want to use the render loop:
clock = new THREE.Clock();
time = clock.getElapsedTime();
INSPECTED.scale.x = time / 1000;
INSPECTED.scale.y = time / 1000;
You can change the divisor based on how fast or slow you want the animation to happen.
One often wants to make a set of objects in Raphael draggable, but using .transform() to do so can be maddening. Say you start like this:
var paper = Raphael(0, 0, 500, 500);
var set = paper.set();
set.push(paper.circle(100,100,35), paper.circle(150,150,15));
set.attr("fill", "orange");
set.data("myset", set);
set.drag(
function(dx, dy, x, y, e) {
this.data('myset').transform("T" + dx + "," + dy);
},
function(x, y, e) {},
function(e) {}
);
If you try this out, you see it works once. But if you drag, stop, then drag again, it resets the position to 0,0 relative to the original position, as you'd expect from .transform(). No good.
A variant of this question has been touched on here, and the respondent suggested prepending transforms with "...". That's all fine and good, but for two things:
you still have to track previous position, since you don't want to
translate by (dx,dy) on every call of dragmove, which will send
the objects flying off the screen.
I worry about creating a monster
transform if an object is dragged many times. (Though maybe I
shouldn't.)
My tentative solution is to track the offset from the original positioning in another key/value pair, like so:
var paper = Raphael(0, 0, 500, 500);
var set = paper.set();
set.push(
paper.circle(100,100,35),
paper.circle(150,150,15)
);
set.attr("fill", "orange");
set.data("myset", set);
set.data("position", [0,0]);
var current_position = [0,0];
set.drag(
function(dx, dy, x, y, e) {
this.data('myset').transform("T" + (this.data("position")[0] + dx) + "," + (this.data("position")[1] + dy));
current_position = [dx,dy];
},
function(x, y, e) {
},
function(e) {
this.data('myset').data("position", [
this.data("position")[0] += current_position[0],
this.data("position")[1] += current_position[1]
]);
}
);
You can see it in action here.
It works, but it feels incredibly sloppy. I must be missing something obvious, right?
My answer is similar to your last variant:
var onmove = function (dx,dy){
this.transform(this.default_transform+'T'+dx+','+dy);
},
onstart = function (){
this.default_transform = this.transform();
},
onend = function(){
this.default_transform = this.transform();
};
set.drag(onmove, onstart, onend);
Don't worry it won't create a long long line of transformations because Raphael converts everything to one whole matrix transformation so it doesn't build up each time you move an object.