I am having an issue with reusing the same button within an HTML5 canvas. The button needs to be reused over the course of several separate frames as well as several times within the same frame.
The button works correctly with the first use:
this.button_13.addEventListener("click", fl_ClickToGoToAndStopAtFrame_24.bind(this));
function fl_ClickToGoToAndStopAtFrame_24()
{
this.gotoAndStop(72);
}
At frame 72, I reuse the same button symbol with a new instance name. Unfortunately, this button does not work:
this.button_14.addEventListener("click", fl_ClickToGoToAndStopAtFrame_25.bind(this));
function fl_ClickToGoToAndStopAtFrame_25()
{
this.gotoAndStop(78);
}
Clicking this second button shows the button states, but will not advance the user to frame 78.
*If the 2nd button exists on the timeline for frame 72 only, it is not present in the published result at all. If the 2nd button exists on frame 72-77, the button is present but not functional.
Any ideas?
Try to do this
this.button_13.removeEventListener("click", fl_ClickToGoToAndStopAtFrame_24.bind(this));
before creating the second event listener.
You have two options here.
Put your separate button instances on different layers.
Create one button instance on its own layer, have a script set the button's visible property (true/false) as needed, and modify your click handler to react based on the value of this.currentFrame.
Animate CC (2015.2 for me at the time of this writing) seems to generate its HTML 5 script output with animation in mind. When you have like object instances on multiple frames on the same layer, instead of creating a new instance of that object, it re-purposes the instance from a prior frame.
e.g. I create a dynamic text box named "myText" on frame 1. On frame 2, I create another dynamic text box and name it "myOtherText". When I publish and look at the script, the script calls Tween.to() first to place the "myText" object on the canvas and give it its appearance and position, then chains another .to() call to make myText look like myOtherText. Under the hood, myOtherText simply doesn't exist.
Creating another layer forces Animate to create the instances explicitly in CreateJS. However, if you have a lot of buttons, that could end up making a ton of layers and making your timeline look messy. I'd recommend option 1 if you only have a couple button instances, but for the latter case, option 2 may be the way to go.
Create a new layer. Name it whatever you want, like "button layer".
This layer will have only one keyframe.
Place your button instance on this layer. For continuity's sake, let's name it button_13.
Bind a tick handler to the root MovieClip (i.e. this) to handle whether the button should appear or not.
var tickHandler = function () {
switch (this.currentFrame) {
//Add case statements for each frame you want the button to appear on
//Remember, frame indexes in CJS are 0-based!
case 12: case 13:
this.button_13.visible = true;
break;
default:
this.button_13.visible = false;
}
}.bind(this);
this.removeEventListener('tick', tickHandler); //Make sure not to double-bind
this.addEventListener('tick', tickHandler);
Bind a click handler to the button that will gotoAndStop on a frame dependent on the current frame.
var clickHandler = function () {
switch (this.currentFrame) {
//Add case statements for each frame that should have a jump
//Remember, frame indexes in CJS are 0-based!
case 12:
this.gotoAndStop(72);
break;
case 13:
this.gotoAndStop(78);
break;
}
}.bind(this) //Proxy the handler, as done above.
this.button_13.removeEventListener('click', tickHandler); //Make sure not to double-bind
this.button_13.addEventListener('click', tickHandler);
Place both of those scripts onto frame 1 of your movie.
I haven't tested this myself as presented, but it should accomplish what you're looking for.
The solution is to move the instance on another layer. In that way, two different instances can be used in different images with different scripts and different names.
Related
Context:
I have my project setup that draws a road on the mapbox as layers (the road is basically a line with styling and painting implemented). Now I need to click an existing line, and wherever I click it, I want to draw a sub-road originating from the point it clicked it
Blue line is Actually line, the surrounding is just styling.
What I want:
I want to get the ID of the line element (or main road), and the point where it is clicked and then when user has clicked on target coordinate on the map, I want to grab that info and add it into existing line so that the line becomes a single with additional breakpoints. This will make the line look like
(the line is single yet it has multiple stops. This makes it consistent. Feels part of line)
The Problem:
I click on the road (the line), and then click somewhere else to stop drawing, I see new road drawn but since it is a separate layer, it looks not consistent with existing one.
Findings:
Every line, every shape is separate layer, since first road, even though it had multiple stops, was in-fact a single road so the design was consistent 3. But since the new line is a new layer, it has its own styling.
I need to find a way to add new data in existing layer
Roadblocks:
Need to find the ID of existing element when I click on it to use it as a source for my new line
Need the pin-point location of click
Need the data for new line/ new layer
find a way to merge into existing layer
Now with all the above context in mind, I am not even able to get the ID of element
SideNote:
While drawing a line in mapbox (using draw.changeMode('draw_line_string'); and then a create various stop points by clicking on map geoJsons) that line stays in one piece even though it has various sub-lines.
Now I need to grab the ID of that line and then specific point when I did click.
Here is my relavent parts of code
draw the line on map
draw.changeMode('draw_line_string');
map.on('draw.create', configureAndUpdateMapBoxDevices);
in configureAndUpdateMapBoxDevices function
draw.add(e.features[0]); //updating event real time for all callbacks where e is passed on as argument so its have these properties.
const json_data = {
json_id: e.features[0].id,
json_string: JSON.stringify(e.features[0])
};
updateMapboxDevice(selected_edit_site_id, json_data); // update on db and models
to style the road defined various variable layers for main road element
map.addLayer(bottomLine);
map.addLayer(left_yellowBelt);
map.addLayer(right_yellowBelt);
map.addLayer(topLayer);
map.addLayer(lineSeparator2L);
three.js r114
What happens is that I want to create a playfield field for field.
When a button is pressed, a new playfield shall be created.
That works.
Before the new playfield is created, the old one has to be deleted.
Then the new playfield shall be created with a little delay after every single part of the field.
I tried a lot to make every change directly visible but there are no changes visible while the program is running.
First I try to make the playfield- object3d invisible so I call the method to do so. The field stays visible.
sample code:
function make_invisible(){
playfield.visible = false;
}
make_invisible();
The field stays visible.
If I return directly after calling that method so my program ends/ class is left, I can see the playfield.
function make_invisible(){
playfield.visible = false;
}
make_invisible();
return;
The field goes invisible.
Same with creating the playfield in two simple for- loops ( row/line).
I take a little delay after every part of the playfield that I add to the group which was previously added to the scene so that every second or so a new part of the playfield should show up on screen.
Here I have the same effect, the playfield is shown completely when the script ends but not after the single parts are inserted.
I hope that sounds familiar to some people.
Thanks in advance.
I've been using the following code in JavaScript for several months and it appears to work without issue, but I started to wonder why it works and whether or not it is a safe method.
The purpose is simply to keep track of the currently selected HTML element and not perform the function code triggered by an event if the user clicks again upon the currently selected element.
function elem_mouse_evt( evt )
{
if ( evt.currentTarget === elem_mouse_evt.e ) return;
elem_mouse_evt.e = evt.currentTarget;
/* ... */
}
This may be a rather stupid question on my part but why is this different than comparing two objects for equivalence? How does JavaScript make this comparison, since each represents a collection of values?
Thank you.
Addition I left out a very important point that came to mind while considering the posted answer, which is that the function property e has a larger purpose. There is a menu of actions that the user can choose to perform on the current selection and/or its content. I thought it would be more efficient to store the reference in the function property rather than traversing the DOM to search for it by one of its unique attributes. Is that a reasonable method?
The purpose is simply to keep track of the currently selected HTML element and not perform the function code triggered by an event if the user clicks again upon the currently selected element.
Rather than doing what you're asking (uniquely identifying an element), I'd flag that element in some way. One way to do that is to add something to its dataset. I'm guessing though that you'll want to change the styling of this selected element at some point though anyway so that users see it's already selected. In that case, we'll add a class.
// Add an event listener on the container for these elements.
// It's more efficient to have just one event listener, rather than thousands.
document.querySelector('.parent').addEventListener('click', (e) => {
// Ensure what's actually clicked meets the filter
if (!e.currentTarget.matches('.selectable')) {
return;
}
// If it's already selected, don't do anything else! (You can combine this with above.)
if (e.currentTarget.matches('.selected')) {
return;
}
e.currentTarget.classlist.add('selected');
// Perform your other actions here
});
I'm working on a simple AR effect for Facebook in Spark AR studio using JavaScript. I have two 3D objects in the scene and I want to switch between them on button click.
So, for example, I have two buttons, and when I click on the first button I want to show the first 3D object (and hide another one). And vice versa - when I click on the second button I want to show the second 3D object and hide the first one.
I can see some examples of how can I access the object in the scene through the script, but I didn't find yet an example of how to create or use buttons in Spark AR.
Is there any easy "drag-and-drop" way to create a button and assign a function to it (like in Unity)? Or should I create an image of the button on the canvas in the scene, use JavaScript to "find" it, detect if the finger touch was made over this image and trigger a function this way?
There is no easy "drag-and-drop" way to create a button and assign a function to it.
You will need to create an image of the button on the canvas in the scene, use Javascript to "find" it, detect if the finger touch was made over this image and trigger a function this way. Here is example code:
var Scene = require('Scene');
var TouchGestures = require('TouchGestures');
var myBtn = Scene.root.find('button');
TouchGestures.onTap(myBtn).subscribe(function() {
//do stuff here
});
Also do not forget to enable the Tap Gesture in your project capabilities settings.
There is also the Native UI Picker that you can make use of:
https://developers.facebook.com/docs/ar-studio/docs/native-ui
(I'm not sure if this was available at the time the question was posted, I'm new to the game)
This is more "drag-and-drop" in that you don't have to create and place the buttons, just assign textures to fill them in, and then you can write a script to do whatever you want when the user selects a button.
I am attempting to mimic the the functionality one would see using the Waze LiveMap (https://www.waze.com/livemap) in Cesium. When a point is clicked, it is converted to a marker with an icon.
I've attempted a few different things with varying levels of success and I'm at my wits end. Does anyone have a good suggestion as to how I can proceed?
There are a couple different ways of doing this, some high-level and some lower-level. The high-level one is easiest so I'll start there. The Cesium Viewer fires an event called selectedEntityChanged when its own selection changes, and you can wire that up to toggle billboard show flags on and off.
Here's a demo. For this demo, I took the original map pins demo and added some lines of code: I turned off the show flags for all the billboards at the start, I added points to stand in for the now-hidden billboards, and I added the following block of code to toggle the billboard show flags when selected, like this:
var lastSelectedPin;
viewer.selectedEntityChanged.addEventListener(function(newEntity) {
if (lastSelectedPin && lastSelectedPin.billboard) {
lastSelectedPin.billboard.show = false;
}
lastSelectedPin = newEntity;
if (lastSelectedPin && lastSelectedPin.billboard) {
lastSelectedPin.billboard.show = true;
}
});
This uses Cesium Viewer's own selection system, but toggles the billboard show flag to appear when selected and disappear when de-selected.
Alternatively, you can dig into Cesium's lower levels for finer control, but there will be a longer learning curve. The Picking Demo shows off several types of picking operations, including scene.pick, scene.drillPick, and camera.pickEllipsoid, which offer various ways to detect what contents exist at a particular screen location. Often these functions are called in response to mouse movements or clicks, or touch/pointer events, to see what the user is interacting with.