How can I stop and start view.onFrame - paperjs

I'm pretty new to paperjs. My animation is working for this I use the following javascript:
view.onFrame = function () {
drawYellowBlock();
}
The function drawYellowBlock draws a yellow block but this animated. When the animation is done I want to stop view.onFrame because I have the feeling it is unnecessary to keep it running while nothing is happening any more. Then when a button is clicked I should be able to activate the onFrame again.
Is this possible and necessary?
So I want my draw function to be something like this:
var scale = 0;
function drawYellowBlock() {
scale = scale + 0.1
//animate block
if(scale < = 1){
//make block grow
}
else{
//stop onFrame
}
$('button').click(function(){
scale = 0;
//start onFrame and Animation
});

You can simply set up a flag used in onFrame method to check if you should animate or not.
Here is a sketch demonstrating the solution.
// Draw the item with a small initial scale.
var item = new Path.Rectangle({
from: view.center - 100,
to: view.center + 100,
fillColor: 'orange',
applyMatrix: false
});
item.scaling = 0.1;
// Draw instructions.
new PointText({
content: 'Press space to start animation',
point: view.center + [0, -80],
justification: 'center'
});
// Create a flag that we will use to know wether we should animate or not.
var animating = false;
// On space key pressed...
function onKeyDown(event) {
if (event.key === 'space') {
// ...start animation.
animating = true;
}
}
// On frame...
function onFrame() {
// ...if animation has started...
if (animating) {
// ...scale up the item.
item.scaling += 0.05;
// When item is totally scaled up...
if (item.scaling.x >= 1) {
// ...stop animation.
animating = false;
}
}
}

You can do something like this
function draw () {
drawYellowBlock();
view.onFrame = undefined
}
view.onFrame = draw
function onclickHandler(){
view.onFrame = draw
}
just remove the function reference from the onFrame handler once its done and append the function back once the button is clicked

Related

button needs to be clicked twice to toggle layer visibility?

Using an example I found on this thread https://gis.stackexchange.com/questions/198896/mapbox-gljs-group-layers/198920#198920?newreg=120a87a082494e41b2e6ab240c94b266 I have grouped multiple layers together and created a single toggle button to turn on and off visibility.
However, the buttons need to be clicked twice to trigger the function. Is it possible to have them only clicked once for the function to work?
Here is my codepen example https://codepen.io/charlie-enright/pen/BarRgbo
//whatever layers you want to toggle go in to this function
toggleLayer(['site location markers'], 'markers');
toggleLayer(['Pen Dinas', 'Rudbaxton','Brawdy Castle', 'Caer Blaen Minog', 'Castell Bach', 'Castell Gwyn', 'Tancredston', 'Thornton Rath', 'Walesland Rath'], 'Geophys');
toggleLayer(['Pendinas 50cm DSM'], '50cm DSM');
toggleLayer(['Pendinas 50cm DTM'], '50cm DTM');
function toggleLayer(ids, name) {
var link = document.createElement('a');
link.href = '#';
link.className = 'active';
link.textContent = name;
link.onclick = function (e) {
e.preventDefault();
e.stopPropagation();
for (layers in ids){
var visibility = map.getLayoutProperty(ids[layers], 'visibility');
if (visibility === 'visible') {
map.setLayoutProperty(ids[layers], 'visibility', 'none');
this.className = '';
} else {
this.className = 'active';
map.setLayoutProperty(ids[layers], 'visibility', 'visible');
}
}
};
var layers = document.getElementById('menu');
layers.appendChild(link);
}
I'm not exactly sure why, but the first time you check the visibility of the layer var visibility = map.getLayoutProperty(ids[layers], 'visibility');, visibility is undefined.
If you check for this in the if statement below, it will toggle on the first click:
if (visibility === 'visible' || visibility === undefined) {
// ^^ if 'visible' or undefined, set visibility to 'none'
...
}

Aframe cardboard movement

I want to be able to move in my VR world by looking around and holding down the cardboard button to move. I have tried for 2 hours and couldn't figure it out. I really don't want to use teleportation as my solution.
I'd throw this in an aframe component, and use the three.js API:
In the init check whether the mouse is up or down.
In the tick find out the rotation as a world matrix using extractRotation(mesh.matrix), apply it to a forward vector using direction.applyMatrix4(matrix), and add it to the current camera position.
AFRAME.registerComponent("foo", {
init: function() {
this.mouseDown = false
this.el.addEventListener("mousedown", (e) => {
this.mouseDown = true
})
this.el.addEventListener("mouseup", (e) => {
this.mouseDown = false
})
},
tick: function() {
if (this.mouseDown) {
let pos = this.el.getAttribute("position")
let mesh = this.el.object3D
var matrix = new THREE.Matrix4();
var direction = new THREE.Vector3(0, 0, -0.1);
matrix.extractRotation(mesh.matrix);
direction.applyMatrix4(matrix)
direction.add(new THREE.Vector3(pos.x, pos.y, pos.z))
this.el.setAttribute("position", direction)
}
}
})
Working fiddle here.

Aframe: cycle through colors using array

I've been trying to cycle through colors using a custom component.
<script>
AFRAME.registerComponent('floor-cycle', {
init: function () {
var sceneEl = document.querySelector('a-scene');
var floorEl = sceneEl.querySelector('#floor')
status = 1;
floorEl.addEventListener('click', function () {
if(status==1) {
floorEl.setAttribute('color', 'red'); status = 2
}
else if(status==2) {
floorEl.setAttribute('color', 'blue'); status = 3;
}
else if(status==3) {
floorEl.setAttribute('color', 'green'); status = 1
}
}
);
}
});
</script>
The component uses status to set the color attribute on click event, however this seems inefficient. Can this be implemented using an array rather than status?
demo - https://codepen.io/MannyMeadows/pen/GWzJRB
You can make an array ['red','green','blue'] and Cycle through it:
colors = ['red','green','blue'];
let i = 0;
floorEl.addEventListener('click',function(){
floorEl.setAttribute('material','color', colors[i]);
function add(){(i==colors.length-1) ? i = 0 : i++;}
add();
});
Seems better as the array is now dynamic, not sure how about the performance.
working fiddle here: https://jsfiddle.net/gftruj/g9wfLgab/2/

Extend polyline and handle mouse event

I write JS app where I draw a lot of polylines using array of points, but in avery point I have some additional properties in this point (GPS data, speed etc).
I want to show these additional props onmouseover or onmouseclick event.
I have two ways:
use the standard polylines and event handler. But in this case I can't to determine additional properties for start point of this polyline cause I can't to save these props in polyline properties. There is one solution - save in array additional properties and try to find them by LatLng of first point of the polyline, but it's too slow I guess..
extend polyline and save additional properties in new Object, but I can't to extend mouse events :(
To extend polyline I use this code:
function myPolyline(prop, opts){
this.prop = prop;
this.Polyline = new google.maps.Polyline(opts);
}
myPolyline.prototype.setMap = function(map) {
return this.Polyline.setMap(map);
}
myPolyline.prototype.getPath = function() {
return this.Polyline.getPath();
}
myPolyline.prototype.addListener= function(prop) {
return this.Polyline.addListener();
}
myPolyline.prototype.getProp= function() {
return this.prop;
}
myPolyline.prototype.setProp= function(prop) {
return this.prop = prop;
}
and create new object in for loop (i - index of current point in array of points) like that:
var polyline_opts = {
path: line_points,
strokeColor: color,
geodesic: true,
strokeOpacity: 0.5,
strokeWeight: 4,
icons: [
{
icon: lineSymbol,
offset: '25px',
repeat: '50px'
}
],
map: map
};
var add_prop = {
id: i,
device_id: device_id
};
...
devices_properties[device_id].tracks[(i-1)] = new myPolyline(add_prop, polyline_opts);
Where:
line_points - array of points (just two points),
i - current point index
devices_properties[device_id].tracks - array of extended polylines (with add properties) by my device_id index
After that I set event handler like that:
var tmp = devices_properties[device_id].tracks[(i-1)];
google.maps.event.addListener(tmp.Polyline, 'click', function(e) {
...
console.log(tmp.prop.id);
...
}
But in this case I always get the same id in console..
When I use
google.maps.event.addListener(devices_properties[device_id].tracks[(i-1)].Polyline, 'click', function(e) {
...
console.log(???); // How to get parent of polyline fired the event?
...
}
I don't know how to get parent of polyline fired the event?
I answer my own question - It's done, I've just have some troubles with using "for" instead "$.each" :)
Before I use:
for ( i = 1; i < devices_properties[device_id].history_points.length; i++ ) {
...
create myPolyline
...
}
and it's doesn't work - created one event handle.
After:
$.each(devices_properties[device_id].history_points, function(i, tmp){
...
create myPolyline ()
...
}
and it works - create a lot of event handlers.
To handle event I use this:
google.maps.event.addListener(c_polyline.Polyline, 'mouseover', function(e) {
var prop = c_polyline.getProp();
...
console.log(prop.id, prop.device_id);
}

Moving a shape in kineticJS towards mouse click

I am experimenting with Meteor and KineticJS. What I'm trying to do is to create a shape, and move it towards a mouse click on the stage. The position should be saved to the mongoDB so that all connected clients can be updated.
I haven't gotten far yet, but this is what I've got. I basically need a way to do two things:
How can I make a shape move towards the mouse click on the stage
and stop when it gets there?
Is there a better way of checking
the current position of a shape, other than the gameLoop that I
tried below? It works, but it feels wrong.
Thank you!
//client.js code
var player = null;
var playerAnim = null;
Template.container.rendered = function () {
var myCanvas = document.getElementById('myCanvas');
var stage = new Kinetic.Stage({
container: myCanvas,
width: 1024,
height: 1024
});
var layer = new Kinetic.Layer();
// add the layer to the stage
stage.add(layer);
// add click listener for the stage
$(stage.getContent()).on('click', function(evt) {
//stage was clicked
//Find the player shape in the database
Players.find().forEach(function (dbShape) {
player = new Kinetic.RegularPolygon(dbShape);
// setup an animation to move the player to the right
playerAnim = new Kinetic.Animation(function(frame) {
var velocity = 50;
var dist = velocity * (frame.timeDiff / 1000);
player.move(dist, 0);
Players.update(dbShape._id, {$set: {x: player.attrs.x, y: player.attrs.y}});
}, layer);
playerAnim.start();
layer.add(player);
layer.draw();
});
});
//make a gameLoop to check the position and stop the animation
Meteor.setInterval(function(gameLoop){
if(player.attrs.x > 500){
playerAnim.stop();
}
}, 500);
Meteor.autosubscribe(function () {
// clear the canvas
if (layer) {
layer.removeChildren();
layer.clear();
}
// populate the canvas with the Shapes collection
Players.find().forEach(function (dbShape) {
var player = new Kinetic.RegularPolygon(dbShape);
layer.add(player);
layer.draw();
});
});
}
I would use a tween to do this. Grab your object, get mouse position, and on mousedown or click create a Tween for your node to that mouse position.
layer.on('mousedown', function() {
var mousePos = stage.getMousePosition();
var tween = new Kinetic.Tween({
node: rect,
duration: 1,
x: mousePos.x,
y: mousePos.y,
opacity: 1,
strokeWidth: 6,
scaleX: 1.5
}).play();
});
JSFiddle
Note: To make the layer clickable, we need to add a transparent rectangle with the size of the width and height of the stage to the layer. See in the jsfiddle the Kinetic.Rect I made named var bg
Would putting the check inside the animation work for you?
playerAnim = new Kinetic.Animation(function(frame) {
var velocity = 50;
var dist = velocity * (frame.timeDiff / 1000);
player.move(dist, 0);
Players.update(dbShape._id, {$set: {x: player.attrs.x, y: player.attrs.y}});
if(player.getX() > 500){
this.stop();
}
}, layer);

Resources