Dynamic modification of snapping values - javascript

I start with interract.js and try to figure out how to snap my elements to defined sizes in the index targets. It works well when I set the values statically but actually I would like to dynamize the values so that each element can snap to each other.
I didn't find how to do this in the doc, would you have a research track allowing me to make the values evolve according to the displacement of the blocks, so that they become "snappable" ?
ideally I think these values should be able to change dynamically each time you move an element, so using dragend event, but how do you make the change in values take effect...
thank you for your feedback.
// here I define my static values for the snap
var dynamicRestrictions = [
{x: 200, range: 20},
{x: 400, range: 20},
{y: 300, range: 20}
];
interact('.resize-drag')
.draggable({
inertia: true,
modifiers: [
interact.modifiers.snap({
targets: dynamicRestrictions, // <= how to dynamic edit them ?
relativePoints: [
{x: 0, y: 0},
{x: 1, y: 1},
{x: 0, y: 1},
{x: 1, y: 0},
],
offset: 'parent'
}),
],
autoScroll: true,
listeners: {
move: dragMoveListener,
}
})

you can use a function on the onstart event.
.draggable({
//...
onstart: dragMoveStartListener,
//...
})
which will allow you to set your hang values.
function dragMoveStartListener(event) {
var element = $(event.target);
dynamicRestrictions.length = 0;
$('.resize-drag').each(function () {
if (!$(this).is(element)) {
var thisPosition = $(this).position(),
snapX = thisPosition.left,
snapY = thisPosition.top,
snapWidth = $(this).width(),
snapHeight = $(this).height(),
range = 20;
var sumWidth = parseInt(snapX + snapWidth),
sumHeight = parseInt(snapY + snapHeight);
dynamicRestrictions.push({x: snapX, y: snapY, range: 10});
dynamicRestrictions.push({x: snapX, range: range});
dynamicRestrictions.push({x: sumWidth, range: range});
dynamicRestrictions.push({y: snapY, range: range});
dynamicRestrictions.push({y: sumHeight, range: range});
}
});
}
I tested it and it works pretty well, except that I don't know how to snap both vertically and horizontally, each time it's one or the other...
looking for a little bit more, should do the job

Related

How to create a random ground in matter.js

I am creating the ground of a game using a Perlin noise function. This gives me an array of vertices. I then add a vertex at the front that is {x:0 y: WORLD_HEIGHT} and another at the end of the array that is {x: WORLD_WIDTH y: WORLD_HEIGHT}. I am hoping that will give me a flat base with a random top.
How then do I add this into the matter.js world?
I am trying to create the ground using;
var terrain = Bodies.fromVertices(???, ???, vertexSets, {
isStatic: true
}, true);
but I don't know what to use for the ??? co-ordinates. I think they are supposed to represent the center of the object. However, I don't know what that is because it is noise. What I would like to do is specify the x & y of the first perlin noise vertex.
I am not even sure that given these vertices matter.js is creating a single body or multiple.
Is this the right way to approach it or there another way to do this? I am really struggling with the docs and the examples.
I use Matter.Body.setPosition(body, position) to override the center of mass and put the ground where I want it based on its bounds property.
const engine = Matter.Engine.create();
const render = Matter.Render.create({
element: document.body,
engine: engine,
});
const w = 300;
const h = 300;
const vertices = [
...[...Array(16)].map((_, i) => ({
x: i * 20,
y: ~~(Math.random() * 40),
})),
{x: w, y: 100},
{x: 0, y: 100},
];
const ground = Matter.Bodies.fromVertices(
w - 10, h - 10, // offset by 10 pixels for illustration
vertices,
{isStatic: true},
/* flagInternal =*/ true,
);
Matter.Body.setPosition(ground, {
x: w - ground.bounds.min.x,
y: h - ground.bounds.max.y + 110,
});
const {min: {x}, max: {y}} = ground.bounds;
console.log(x, y); // 10 120
Matter.Composite.add(engine.world, [ground]);
Matter.Render.run(render);
Matter.Runner.run(engine);
<script src="https://cdn.jsdelivr.net/npm/poly-decomp#0.3.0/build/decomp.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.18.0/matter.min.js"></script>
Without setPosition, you can see things jump around if you run this snippet a few times (just to reproduce OP's error with a concrete example):
const engine = Matter.Engine.create();
const render = Matter.Render.create({
element: document.body,
engine: engine,
});
const vertices = [
...[...Array(16)].map((_, i) => ({
x: i * 20,
y: ~~(Math.random() * 40),
})),
{x: 300, y: 100},
{x: 0, y: 100},
];
const ground = Matter.Bodies.fromVertices(
200, 100, vertices,
{isStatic: true},
/* flagInternal =*/ true,
);
Matter.Composite.add(engine.world, [ground]);
Matter.Render.run(render);
Matter.Runner.run(engine);
<script src="https://cdn.jsdelivr.net/npm/poly-decomp#0.3.0/build/decomp.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.18.0/matter.min.js"></script>
I'm not using Perlin noise and there are some internal vertices that aren't properly detected in the above examples, but the result should be the same either way.
should be integers, all width and height of the noise texture. values at those x, y integer places can be floats... no problem.
and same width and height should go to terrain and values at that places will be the height of the terrain.

How to set the max value in the heatmap properly setData?

I have some data saved and it is huge, 100.000 plus points saved in the database. In this format:
database_data = [
{ x: 100, y: 100, value: 1},
{ x: 200, y: 100, value: 1},
{ x: 120, y: 320, value: 1},
...
]
I am giving the value of 1 for each point as you can see above. I would like to use:
heatmap.setData({
max: max_value,
data: database_data
});
To plot the data on the screen. How can I calculate the max_value of the heatmap? Because I wanna have the information been displayed on the screen where the most red color is the points where we have more frequently in the database and the less red color would be the point where we have only once in the database. But as we know, (x:100, y:100) and x:120, y: 120) will have some intersection between them, and that would also need to be consider, just like the normal heat map.
In other words, I want to know how to automatically calculate the value for "max" in the heatmap using setData function!
Here below it is a function that is working fine, but it takes too much time:
var body = document.body;
var bodyStyle = getComputedStyle(body);
var hmEl = document.querySelector(".heatmap-wrapper");
hmEl.style.width = bodyStyle.width;
hmEl.style.height = bodyStyle.height;
hmEl.style.background = "#3e1852";
hmEl.style.zIndex = "1000";
var hm = document.querySelector(".heatmap");
var heatmap = h337.create({
container: hm,
radius: 40,
});
var dbh_x = <?php echo $dbh_x;?>; //LIKE THIS->["100", "200", "300","400", "420", "500", "600", "600"];
var dbh_y = <?php echo $dbh_y;?>;//LIKE THIS->["100", "200", "300","400", "420", "500", "600", "600"];
for ( var i =0; i< dbh_x.length; i++){
heatmap.addData({
x: dbh_x[i],
y: dbh_y[i],
});
};
Because of the loading time, I was trying to use setData instead:
var body = document.body;
var bodyStyle = getComputedStyle(body);
var hmEl = document.querySelector(".heatmap-wrapper");
hmEl.style.width = bodyStyle.width;
hmEl.style.height = bodyStyle.height;
hmEl.style.background = "#3e1852";
hmEl.style.zIndex = "1000";
var hm = document.querySelector(".heatmap");
var heatmap = h337.create({
container: hm,
radius: 40,
});
heatmap.setData({//This function will show the data kind a fast for 100000+points
max: 10, //This is the problem
data: [{ x: 891, y: 50, value: 1},{ x: 891, y: 50, value: 1},...]
});
Appreciate your guys time!

How do i render a tween to a sequence of png files with Konva JS on Node?

As an experiment I want to animate a square with the Tween class and save all the frames as png files, however a proper callback such as onUpdate doesn't seem to exist. I would like to call it on every new frame and generate a png of the current state with a function I have already created or via another solution.
The code below is based on an example I found in this repository on Github.
let i = 0;
let tween = new Konva.Tween({
node: rect,
duration: 1,
x: 140,
y: 90,
rotation: Math.PI * 2,
opacity: 1,
strokeWidth: 6,
scaleX: 1.5,
onUpdate: () => { // does not exist
i++;
console.log(i);
render_frame(i);
},
onFinish: () => {
console.log("finished");
}
});
tween.play();
There is no onUpdate callback, but we can use the draw event of layer to save the image:
let tween = new Konva.Tween({
node: circle,
duration: 1,
x: 140,
y: 90,
rotation: Math.PI * 2,
opacity: 1,
strokeWidth: 6,
scaleX: 1.5,
onFinish: () => {
// remove draw event
console.log('finish');
layer.off('.tween');
}
});
tween.play();
layer.on('draw.tween', () => {
// save layer to image
console.log('to image')
})
https://jsbin.com/regiduzusi/1/edit?html,js,console,output
Update:
If you need synchronous updates you can change tween time manually.
const duration = 1;
const tween = new Konva.Tween({
node: circle,
duration: duration,
x: 140,
y: 90,
rotation: Math.PI * 2,
opacity: 1,
strokeWidth: 6,
scaleX: 1.5
});
const FPS = 25;
const tics = FPS * duration;
for (let i = 0; i < tics; i++) {
tween.seek(i / tics);
layer.draw();
console.log('to image')
}
Demo 2: https://jsbin.com/fudanozani/1/edit?html,js,console,output

ScrollMagic + TweenMax: Draw multiple SVG paths simultaneously?

This is my script for drawing an SVG path with ScrollMagic:
// Prepare SVG
function pathPrepare_journey($el) {
var lineLength_journey = $el[0].getTotalLength();
$el.css("stroke-dasharray", lineLength_journey);
$el.css("stroke-dashoffset", lineLength_journey);
}
var $journey1 = $("path#path1");
var $journey1_2 = $("path#path2");
var $journey1_3 = $("path#path3");
pathPrepare_journey($journey1);
pathPrepare_journey($journey1_2);
pathPrepare_journey($journey1_3);
// Reference to container
var container = $("#section1");
var containerHeight = $(container).height();
var vpHeight = $(window).height();
// Init controller
var SVGcontroller_journey = new ScrollMagic.Controller();
// Build tween
var tween_journey = new TimelineMax().add(
TweenMax.to($journey1, 1, { strokeDashoffset: 0, ease: Linear.easeNone })
);
// Build scene
var scene = new ScrollMagic.Scene({
triggerElement: container,
duration: containerHeight - vpHeight / 2,
tweenChanges: true
})
.setTween(tween_journey)
.addIndicators({
name: "Draw Journey Lines#1",
colorTrigger: "brown",
colorStart: "brown",
colorEnd: "brown",
indent: 600
})
.addTo(SVGcontroller_journey);
It works perfectly fine, but as you can see, I have three individual paths inside my SVG ($journey1,$journey1_2 & $journey1_3), and the ScrollMagic scene currently only draws one of them, $journey1, because I was only able to add that one to the TimelineMax().
My simple question: How do I add the other paths so they are drawn at the same time as $journey1?
I was able to add the other paths, but they are being drawn consecutively:
// Build tween
var tween_journey = new TimelineMax()
.add(
TweenMax.to($journey1, 1, { strokeDashoffset: 0, ease: Linear.easeNone })
)
.add(
TweenMax.to($journey1_2, 1, { strokeDashoffset: 0, ease: Linear.easeNone })
);
I figured out, that I can target paths by class inside the TweenMax timeline like so:
var tween_journey = new TimelineMax();
tween_journey.to(".cls-2", 1, { strokeDashoffset: 0, ease: Linear.easeNone });
This animates/draws all paths of the given class at the same time and hence solves my problem.

fabric js: Polygon perPixelTargetFind

I'm creating a Polygon with fabric.js and set its value "perPixelTargetFind" true.
When you click, for example, on the right top end (not inside the polygon, inside its bounding box), the polygon gets selcected, although "perPixelTargetFind" is set ( I though with this value it is only possible to select an object by directly clicking on it).
The polygon should only be selected, if you click directly on it, is this possible?
Here is a link to the issue on jsfiddle: Polygon perPixelTargetFind
And here is my code so far:
var canvas = new fabric.Canvas('canvas');
fabric.Object.prototype.originX = fabric.Object.prototype.originY = 'center';
document.getElementById("canvas").tabIndex = 1000;
var pol = new fabric.Polygon([
{x: 200, y: 0},
{x: 250, y: 50},
{x: 250, y: 100},
{x: 150, y: 100},
{x: 150, y: 50} ], {
left: 250,
top: 150,
fill: 'green',
perPixelTargetFind: true
}
);
canvas.add(pol);
thank you for your help! :)

Categories

Resources