a-frame Trigger event using aframe-alongpath-component - aframe

In a-frame using aframe-alongpath-component i would like to count each loop of an animation.
I think event movingended is not triggered with a loop?!
How can i use alongpath-trigger-activated when element is reaching a-curve-point X (maybe endpoint)?

My assumption was wrong. With every loop movingended is triggered.
let ball = document.createElement('a-sphere');
ball.setAttribute('id', `Ball_${a}`);
ball.setAttribute('class', 'clickable');
ball.setAttribute('src', `#tBall_${a}`);
ball.setAttribute(`alongpath`, `curve: .track${a}; dur: ${pathDuration}; delay: ${startdelay}; loop: true ;`);
ball.addEventListener("movingended", (e) => {
console.log("moving ended:" + bad_hits);
bad_hits++;
});
So i don't need alongpath-trigger-activated. But to answer my question:
let track = document.createElement('a-curve');
track.setAttribute('class', `track${a}`);
scene.append(track);
...
let point5 = document.createElement('a-curve-point');
point5.setAttribute('position', '0 3 -5');
point5.setAttribute('class', 'trigger');
point5.addEventListener("alongpath-trigger-activated", () => {
console.log("point 5 alongpath-trigger-activated");
});
track.append(point5);

Related

Retrieve and compare the style attribute of an element periodically using using cypress

I have a time indicator that travels over a timescale, the indicator's style attribute value keeps on changing for every x milliseconds and I need to get, store and compare that the previously captured value is greater than the latest value.
Initial value:
Latest value:
The logic is, from one point (left 10), every second it moves to the left (left -0, -1, -2, -3 ...)
I tried few ways and one of them is to capture in the same 'cy.then', but in that case, the element will not have the recent value. So far, I tried this. it fetches the value and with some help of regex, I got a 'comparable' value but how I can store/compare those values? Additionally, what is the best way if we need to compare more than 2 values?
const BTN_CONTROL_TIMEINDICATOR = '#currentTimeIndicator'
static verifyTimeLapse() {
//wip
var initialVal, nextVal
initialVal = this.getAnyValueOfAnElement(BTN_CONTROL_TIMEINDICATOR)
cy.wait(500)
nextVal = this.getAnyValueOfAnElement(BTN_CONTROL_TIMEINDICATOR)
cy.log(initialVal > nextVal)
}
static getAnyValueOfAnElement(element) {
//wip
cy.get(element)
.then(($ele) => {
const val=$ele.attr('style').replace(/[^\d.-]/g, '')
cy.log(val)
// return does not work
})
}
cy.log:
Page objects don't work very well with the Cypress command queue, here's what you might do with custom commands.
/* Get the numeric value of CSS left in px */
Cypress.Commands.add('getTimescaleValue', () => {
cy.get('#currentTimeIndicator')
.then($el => +$el[0].style.left.replace('px',''))
})
/* Get a sequence of time scale values */
Cypress.Commands.add('getTimescaleValues', ({numValues, waitBetween}) => {
const values = [];
Cypress._.times(numValues, () => { // repeat inner commands n times
cy.getTimescaleValue()
.then(value => values.push(value)) // save value
.wait(waitBetween)
})
return cy.wrap(values);
})
/* Assert a sequence of values are in descending order */
Cypress.Commands.add('valuesAreDescending', { prevSubject: true }, (values) => {
values.reduce((prev, current) => {
if (prev) { // skip first (no prev to compare)
expect(prev).to.be.gt(current) // assert pairs of values
}
return current
});
})
it('check the timeline', () => {
cy.getTimescaleValues({ numValues: 10, waitBetween: 100 })
.valuesAreDescending()
})
Log
assert
expected 63 to be above 58
assert
expected 58 to be above 48
assert
expected 48 to be above 43
assert
expected 43 to be above 33
assert
expected 33 to be above 23
assert
expected 23 to be above 18
assert
expected 18 to be above 13
assert
expected 13 to be above 3
assert
expected 3 to be above -2
Tested with
<div id="currentTimeIndicator" style="left:63px">Target</div>
<script>
const timer = setInterval(() => {
const div = document.querySelector('#currentTimeIndicator')
const left = +div.style.left.replace('px', '');
if (left < 0) {
clearInterval(timer)
return
}
const next = (left - 5) + 'px';
div.style.left = next;
}, 100)
</script>
If your app uses setInterval() for timing, you should be able to use cy.clock() and cy.tick() instead of .wait(waitBetween) to get more precise sampling and faster test execution.
I don't know where the initial value comes from. But before it changes, maybe on page load, maybe as first job on click, etc you can do something like this:
let item = document.querySelector("#currentTimeIndicator");
item.dataset.left = parseFloat(item.style.left);
console.log(item);
<div id="currentTimeIndicator" style="left:-20px"></div>

Triggering mouse/touch events in Matter.js

How would one go about adding programmatically triggered touch/mouse events in Matter.js? I have a few collision events set up for the engine, but can not trigger a mouseup event that stops the current dragging action. I've tried various combinations of targeting the canvas element, the mouse/mouseConstraint, and the non-static body.
If you, like me, came here trying to figure out how to be able to click on a Matter.js body object, let me give you one way. My goal in my project was to assign some attributes to my rectangle objects and call a function when they were clicked on.
The first thing to do was to distinguish between dragging and clicking, so I wrote(using Jquery):
$("body").on("mousedown", function(e){
mouseX1 = e.pageX;
mouseY1 = e.pageY;
});
$("body").on("mouseup", function(e){
mouseX2 = e.pageX;
mouseY2 = e.pageY;
if((mouseX1 == mouseX2) && (mouseY1 == mouseY2)){
//alert("click!\n" + mouseX2 + " " + mouseY2 +"\n");
var bodiesUnder = Matter.Query.point(books, { x: mouseX2, y: mouseY2 });
//alert("click!\n" + mouseX2 + " " + mouseY2 +"\n");
if (bodiesUnder.length > 0) {
var bodyToClick = bodiesUnder[0];
alert(bodyToClick.title2);
}
}
});
This was accomplished when listening for "mouseup" and asking if ((mouseX1 == mouseX2) && (mouseY1 == mouseY2)).
Second- the juicy part- create a var array to hold the objects, or 'bodies', we are going to dig up under the mouse. Thankfully there's this function:
var bodiesUnder = Matter.Query.point(books, { x: mouseX2, y: mouseY2 });
For the first element in here I entered "books". For you this needs to be the name of an array you've put all your objects, or 'bodies' into. If you don't have them in an array, it's not hard to throw them all in, like so:
var books = [book1, book2, book3];
Once that was all done, I was able to alert(book1.title2) to see what the title of that book (body) is. My bodies were coded as follows:
var book2 = Bodies.rectangle(390, 200, 66, 70, {
render : {
sprite : {
texture: "img/tradingIcon.jpg"
}
},
restitution : 0.3,
title1 : 'Vanessa and Terry',
title2 : 'Trading'
});
Hope that helps! This one had me hung up for a whole day.
It turns out I had incorrectly configured the Matter.Mouse module, and was re-assigning the mouse input that had already been set in MouseConstraint. The following works in regards to my original question:
Matter.mouseConstraint.mouse.mouseup(event);

Meteor/ng2: Where to process a variable that requires two subscription to be resolved

Assume I have
this.subscribe('a', () => {this.allA = A.find();});
this.subscribe('b', () => {this.allB= B.find();});
And a variable that is something like
let x = *take the first A, do some calculation to get the B linked, return B*
Where such logic be put to be sure this is only processed when subscriptions 'a' and 'b' are resolved ?
Might be using zones but I am not 100% what could be the best way to do it in a #Component.
PS: avoid do-a-serverside-method :D
Regards
Instead of using callbacks in your subscriptions, return subscription handles and then check their ready() state:
const subA = this.subscribe('a');
const subB = this.subscribe('b');
const that = this;
Tracker.autorun(()=>{
if ( subA.ready() ) that.allA = A.find();
if ( subB.ready() ) that.allB = B.find();
if ( subA.ready() && subB.ready() ) {
let x = ... //compute x
}
});
The solution seems for now to be to wrap it in a autorun (MeteorComponent for instance)
this.autorun(()=>{
let s1 = this.subscribe('a', () => {this.allA = A.find();});
let s2 = this.subscribe('b', () => {this.allB= B.find();});
if(s1.ready() && s2.ready())
...

Simple Timer in Meteor JS

I am creating a simple countdown timer for a game. I am using CoffeeScript and Meteor. I have a Handlebars "Timer" template with a {{time}} expression.
Here is the code:
clock = 10
timeLeft = () ->
if clock > 0
clock--
else
"That's All Folks"
Meteor.clearInterval(interval)
interval = Meteor.setInterval(timeLeft, 1000)
if Meteor.isClient
Template.timer.time = interval
The above code just gives me a static display of 8 or 6 instead of the countdown timer.
If I add a few console.log statements I can see it work as designed in the terminal.
clock = 10
timeLeft = () ->
if clock > 0
clock--
console.log clock
else
console.log "That's All Folks"
Meteor.clearInterval(interval)
interval = Meteor.setInterval(timeLeft, 1000)
if Meteor.isClient
Template.timer.time = interval
If you want to update a value in handlebars you need to use Session so that its reactive, otherwise the Templating system won't be aware of when to update it in the ui. Also you passed the template a handler thats the handle instead of the timer value.
Using the below, I've used Session to pass this data through to handlebars.
clock = 10
timeLeft = ->
if clock > 0
clock--
Session.set "time", clock
console.log clock
else
console.log "That's All Folks"
Meteor.clearInterval interval
interval = Meteor.setInterval(timeLeft, 1000)
if Meteor.isClient
Template.timer.time = ->
Session.get "time"
Also in javascript in case anyone else wants this:
var clock = 10;
var timeLeft = function() {
if (clock > 0) {
clock--;
Session.set("time", clock);
return console.log(clock);
} else {
console.log("That's All Folks");
return Meteor.clearInterval(interval);
}
};
var interval = Meteor.setInterval(timeLeft, 1000);
if (Meteor.isClient) {
Template.registerHelper("time", function() {
return Session.get("time");
});
}
In essence you tell Session the time value, and when its updated it tells the templating system to redraw with the updated time value.

Fullcalendar - limit selectable to a single day

By default if you enable the 'selectable' attribute it will allow you to click and drag and select several days. I would like to only allow the user to select a single day, not drag over multiple. Is there a way to have 'selectable' enabled, but disable the dragging feature that comes along with it?
If you want to limit highlight to a single day in agenda week view you can use following:
selectConstraint:{
start: '00:01',
end: '23:59',
},
if you want to limit the event you can use
eventConstraint:{
start: '00:00',
end: '24:00',
},
in the select callback, adding the following does the trick:
(fullcalendar 2 using moment.js)
if (start.add('days', 1).date() != end.date() )
$scope.eventCal.fullCalendar('unselect');
resources:
http://arshaw.com/fullcalendar/docs/selection/select_callback/
http://arshaw.com/fullcalendar/docs/selection/unselect_method/
You can select a single date or time by passing fullcalendar's 'select' method to the dayClick event listener:
$('#myCalendar').fullcalendar({
dayClick: function(date,jsEvent,view) {
$('#myCalendar').fullcalendar('select', date);
}
});
Note you will also need to fire the 'unselect' method on your next callback (or dayClick).
Why not use selectAllow?
Start by converting the start and end times to seconds. Compare that to the number of seconds in a day.
Working Solution Without Using Moment.js:
selectAllow: function (e) {
if (e.end.getTime() / 1000 - e.start.getTime() / 1000 <= 86400) {
return true;
}
}
This configuration setting worked for me on FullCalendar v5:
selectAllow: function(selectionInfo) {
let startDate = selectionInfo.start;
let endDate = selectionInfo.end;
endDate.setSeconds(endDate.getSeconds() - 1); // allow full day selection
if (startDate.getDate() === endDate.getDate()) {
return true;
} else {
return false;
}
}
simply :
selectAllow: function (selectInfo) {
return selectInfo.end.diff(selectInfo.start, 'days') == 1;
}
For me using the selectAllow option like this worked
selectAllow: function(selectionInfo) {
// Don't allow creation of events over more than 1 day
return moment(selectionInfo.start).utcOffset(false).isSame(moment(selectionInfo.end).subtract(1, 'second').utcOffset(false), 'day');
},
I used utcOffset(false) because for whatever reason it doesn't work reliably without it and I used subtract(1, 'second') because the end date is inclusive, so without it you can't select the end of the day
This will be executed only when the user selects a day
// ...
select: function(start, end){
if(moment(start._d).add(1, 'days').format('YYYY-MM-DD')==moment(end._d).format('YYYY-MM-DD')){
// just select one day
}
},
// ...
I could do this using validRange:
https://fullcalendar.io/docs/validRange
Not at this time: the range of selectable days can not be customized without modifying the source.

Resources