Get GenericSync per Surface or View - javascript

I want to do a scroll list with bunch of surface images. Then when I click or touch on a surface, I want to get the surface object being affected and do something with it (like change content).
The way I setup below is to have scrollview -> container -> view -> modifier -> surface. I don't know if it is efficient or not (maybe redundant). But I cannot get the exact object triggering event in console.log(data) of each sync object.
define(function(require, exports, module) {
var Engine = require('famous/core/Engine');
var Surface = require('famous/core/Surface');
var ContainerSurface = require("famous/surfaces/ContainerSurface");
var View = require('famous/core/View');
var Scrollview = require('famous/views/Scrollview');
var Transform = require('famous/core/Transform');
var Modifier = require("famous/core/Modifier");
var GenericSync = require("famous/inputs/GenericSync");
var MouseSync = require("famous/inputs/MouseSync");
var TouchSync = require("famous/inputs/TouchSync");
var mainContext = Engine.createContext();
GenericSync.register({
"mouse" : MouseSync,
"touch" : TouchSync
});
var scrollview = new Scrollview({});
var listContainer = [];
var questionContainer = [];
var imgSurface = [];
var sync = [];
var imgModifier = new Modifier({origin: [0.5, 0.5]});
for (var i=0; i<10; i++) {
questionContainer[i] = new ContainerSurface({
size: [undefined, 300],
properties:{
overflow: 'hidden'
}
});
imgSurface[i] = new Surface({
content: '<img width="100%" src="http://placehold.it/400x300">'
});
sync[i] = new GenericSync(
["mouse", "touch"],
{direction : GenericSync.DIRECTION_X}
);
questionContainer[i].add(imgModifier).add(imgSurface[i]);
questionContainer[i].pipe(sync[i]);
questionContainer[i].pipe(scrollview);
sync[i].on("end", function(data) {
console.log(data);
});
listContainer.push(questionContainer[i]);
}
scrollview.sequenceFrom(listContainer);
mainContext.add(scrollview);
});
So how do I access the triggering surface or view in this case? I was hoping to get something like data.triggerObject.setContent() that referenced back to that particular surface in the array. Well, such thing doesn't seem to exist...
UPDATE 1:
The answer doesn't need to follow my code above. My "high level" goals are:
Generate a list of surfaces with images. Data from some array.
Detect click AND touch event per surface and do something with that surface
Basically, I tried to do an "advance" version of this example https://github.com/Famous/examples/blob/master/src/examples/core/View/example.js

So GenericSync is working as an aggregator of the the Sync classes. The Sync classes give you useful information based on simpler events such as mousedown and mousemove. That means if you want more information, you are going to have to make MouseSync, for example, give it up.
If you do not wish to write your own MouseSync class, just take a look at where MouseSync emits its events. In the event handling function, you may access the original event. You can attach whatever you wish to the payload that is emitted.. Take for example _handleEnd in MouseSync.js
function _handleEnd(event) {
if (!this._prevCoord) return;
// I added this line..
this._payload.origin = event.origin;
this._eventOutput.emit('end', this._payload);
this._prevCoord = undefined;
this._prevTime = undefined;
}
If it feels too strange editing the source directly and tracking changes, you may just want to duplicate MouseSync to a different classname eg. MyMouseSync and make changes to only that file. The same logic applies to TouchSync.
Hope this helps!
EDIT TO YOUR UPDATED QUESTION:
You can use the default events available through famous surfaces. These include the origin property in the payload.
mousedown, mousemove mouseup, touchstart, touchmove, touchend
surface.on('touchstart',function(e){
touchedSurface = e.origin;
});
In most of these cases, I would detect touch with something like Modernizr, and only apply the necessary events. Good Luck!

Related

Offscreen-Canvas not rendering some of the time

I am trying to implement a in browser raster drawing plugin for the leaflet library that that extends the leaflets GridLayer api. Essentially for every tile there is function createTile that returns a canvas with some drawing on it. and leaflet shows the tile in correct position.
initialize: function(raster_data){
this.raster_data = raster_data;
},
createTile: function (tile_coords) {
let _tile = document.createElement('canvas');
let _tile_ctx = _tile.getContext('2d');
// do some drawing here with values from this.raster_data
return _tile;
}
This implementation is so far working fine. Than I thought of offloading drawing with offscreen-canvas in a webworker. so I restructured the code like this
initialize: function(raster_data){
this.raster_data = raster_data;
this.tile_worker = new Worker('tile_renderer.js')
},
createTile: function (tile_coords) {
let _tile = document.createElement('canvas').transferControlToOffscreen();
this.tile_worker.postMessage({
_tile: _tile,
_raster_data: this.raster_data
},[_tile])
return _tile;
}
This works but every now and then i see a canvas that is just blank. That thing is quite random I don't know start from where and how should I debug this. can this be a problem that I am using a single worker for rendering every tile? any help is appreciated. Here is an example of a blank canvas.
This a known bug: https://crbug.com/1202481
The issue appears when too many OffscreenCanvases are sent to the Worker serially.
The workaround is then to batch send all these OffscreenCanvases in a single call to postMessage().
In your code you could achieve this by storing all the objects to be sent and use a simple debouncing strategy using a 0 timeout to send them all at once:
createTile: function (tile_coords) {
let _tile = document.createElement('canvas');
_tile.setAttribute('width', 512);
_tile.setAttribute('height', 512);
let _off_tile = _tile.transferControlToOffscreen();
this.tiles_to_add.push( _off_tile ); // store for later
clearTimeout( this.batch_timeout_id ); // so that the callback is called only once
this.batch_timeout_id = setTimeout( () => {
// send them all at once
this.tileWorker.postMessage( { tiles: this.tiles_to_add }, this.tiles_to_add );
this.tiles_to_add.length = 0;
});
return _tile;
}
Live example: https://artistic-quill-tote.glitch.me/

How can I add eventlisteners to content that has been updated from localstorage?

I am working on a web application that allows for divs to be drag and dropped/rearranged. The new order of the divs is stored in local storage and if the page is refreshed or navigated away from and then back to, the user's chosen order is then fetched from local storage and displayed to the page. So far I have most of these things working, however when the page is refreshed/navigated away from and back to, the divs are no longer able to be moved around. In the console it appears that the event listeners are no longer on the draggable targets. How can I structure/edit my code so that the divs can be rearranged even if the user has refreshed or come back to the page at a later point in time? (I think the way I am handling events currently is also not very good, so suggestions for how to improve that are more than welcome.)
// get two groups of elements, those that are draggable and those that are drop targets
var draggable = document.querySelectorAll('[draggable]');
var targets = document.querySelectorAll('[data-drop-target]');
// div immediately surrounding bus routes
var busList = document.getElementById("bus-list");
var busListBuses = busList.children;
// make array from busListBuses which is an htmlCollection (not currently using this)
var divArr = Array.from(busListBuses);
// store the id of the draggable element when drag starts
function handleDragStart(e) {
e.dataTransfer.setData("text", this.id); // sets 'text' value to equal the id of this
this.classList.add("drag-start"); // class for styling the element being dragged
}
function handleDragEnd(e) {
e.target.classList.remove('drag-start');
}
function handleDragEnterLeave(e) {
//
}
// handles dragover event (moving your source div over the target div element)
// If drop event occurs, the function retrieves the draggable element’s id from the DataTransfer object
function handleOverDrop(e) {
e.preventDefault();
var draggedId = e.dataTransfer.getData("text"); // retrieves drag data (DOMString) for specified type
var draggedEl = document.getElementById(draggedId);
draggedEl.parentNode.insertBefore(draggedEl, this); // inserts element being dragged into list
var draggedArray = Array.from(draggedEl.parentNode.children); // creates new array which updates current location of each route
if (e.type === "drop") {
// when dropped, update localstorage
savePage(draggedArray);
}
}
// get each full bus-route div in #bus-list with p content as single arr item each
// called when item is dropped
function savePage(dArray) {
// honestly i can't remember what this does precisely
// but i can't seem to add to localstorage in the way i want without it
var arr = Array.prototype.map.call(dArray, function(elem) {
return elem.outerHTML;
});
localStorage.newList = JSON.stringify(arr); // have to stringify the array in order to add it to localstorage
}
// ideally it should just update the order of the bus routes to reflect local storage
// and add classes/ids to the divs etc. (hence using outerHTML)
function restorePage() {
// getting the item from localstorage
var getData = localStorage.getItem("newList");
// parsing it back into an array
var parsedData = JSON.parse(getData);
// string to hold contents of array so they can be display via innerHTML
var fullList = "";
if (localStorage.getItem("newList") === null) {
return; // maybe this is wrong but if there's nothing in local storage, don't do anything
} else {
for (let i = 0; i < parsedData.length; i++) {
fullList = fullList + parsedData[i];
}
busList.innerHTML = fullList;
}
}
// submit button to save changes to db
// probably better way to do this
for (let i = 0; i < draggable.length; i++) {
draggable[i].addEventListener("dragstart", handleDragStart);
draggable[i].addEventListener("dragend", handleDragEnd);
}
// drop target elements
for (let i = 0; i < targets.length; i++) {
targets[i].addEventListener("dragover", handleOverDrop);
targets[i].addEventListener("drop", handleOverDrop);
targets[i].addEventListener("dragenter", handleDragEnterLeave);
targets[i].addEventListener("dragleave", handleDragEnterLeave);
}
window.addEventListener('load', function(){
restorePage();
});
I am using React with Pug, here is the Pug file in case that's helpful.
html
head
title
link(rel='stylesheet', type='text/css', href='stylesheets/normalize.css' )
link(rel='stylesheet', type='text/css', href='stylesheets/style.css' )
body
div.container
div#bus-list
for route in routeInfo
- var routeID = route.ID
- var routePos = route.position
div(class=route.type, draggable="true", data-drop-target="true", id=`r-${routePos}`).bus-route
p #{route.route} #{route.route_name}
button(type="submit", value="submit") done!
script(src='js/styling.js')
I think problem is in the sequence of your code.
JavaScript code is bening parsed as script. So browser executes commands during script loading.
How it works:
Browser loads DOM without any items
Browser loads JS and adds listeners to the elements (which aren't loaded yet)
Brower fires onLoad() and your script loads elements from the storage.
End of the cycle.
You need to add listeners after you load the elements. Not before that.
Off topic
You do some repetitive calls.
You do
var getData = localStorage.getItem("newList");
var parsedData = JSON.parse(getData);
var fullList = "";
if (localStorage.getItem("newList") === null) {
return;
}
Instead of
var getData = localStorage.getItem("newList");
if (getData === null) {
return;
}
var parsedData = JSON.parse(getData);
var fullList = "";
It hurts readability and perfomance.
I would suggest you to read some books about C languague and Code Style. That will help you on your journey.

Confused with Viewer API vs example, passing options to extensions

I am confused as the examples on how to use the Viewer don't seem to match the documentation of the API, some functions are not in the docs or their signature is different.
Base on the examples code, how do I pass options to the extensions I instantiate? I would like to pass my extension a callback.
Thanks!
We need to fix the doc so it does not rely anymore on the undocumented A360 viewer additional code, which is supposed to be internal. Sorry for the incovenience, we will do this asap...
For the time being, you can use the code from my viewer boilerplate sample:
function initializeViewer(containerId, urn) {
Autodesk.Viewing.Document.load(urn, function (model) {
var rootItem = model.getRootItem();
// Grab all 3D items
var geometryItems3d = Autodesk.Viewing.Document.getSubItemsWithProperties(
rootItem,
{ 'type': 'geometry', 'role': '3d' },
true);
// Grab all 2D items
var geometryItems2d = Autodesk.Viewing.Document.getSubItemsWithProperties(
rootItem,
{ 'type': 'geometry', 'role': '2d' },
true);
var domContainer = document.getElementById(containerId);
//UI-less Version: viewer without any Autodesk buttons and commands
//viewer = new Autodesk.Viewing.Viewer3D(domContainer);
//GUI Version: viewer with controls
viewer = new Autodesk.Viewing.Private.GuiViewer3D(domContainer);
viewer.initialize();
viewer.setLightPreset(8);
//Button events - two buttons to load/unload a sample extension
// Irrelevant to viewer code itself
var loadBtn = document.getElementById('loadBtn');
loadBtn.addEventListener("click", function(){
loadExtension(viewer);
});
var unloadBtn = document.getElementById('unloadBtn');
unloadBtn.addEventListener("click", function(){
unloadExtension(viewer);
});
// Illustrates how to listen to events
// Geometry loaded is fired once the model is fully loaded
// It is safe to perform operation involving model structure at this point
viewer.addEventListener(
Autodesk.Viewing.GEOMETRY_LOADED_EVENT,
onGeometryLoaded);
//optional
var options = {
globalOffset: {
x: 0, y: 0, z: 0
}
}
// Pick the first 3D item ortherwise first 2D item
var viewablePath = (geometryItems3d.length ?
geometryItems3d[0] :
geometryItems2d[0]);
viewer.loadModel(
model.getViewablePath(viewablePath),
options);
}, function(err) {
logError(err);
});
}
Once the viewer is initialized, you can load independently each extension and pass a callback as follow:
var options = {
onCustomEventFiredByMyExtension: function() {
console.log('LMV rulez!')
}
}
viewer.loadExtension('MyExtensionId', options)
But I think a more elegant approach would be to fire events from the extension itself, which may look like this:
viewer.loadExtension('MyExtensionId')
var myExtension = viewer.getExtension('MyExtensionId')
myExtension.on('CustomEvent', function () {
console.log('LMV still rulez!')
})
See micro-events for a super simple event library.

focusControl.focusAsync() - Catastrophic failure

I'm writing a WP8.1-App in JavaScript/HTML that needs a working camera.
My mediaCapture is set up and working. So I tried to do the focus:
var focusControl = mediaCapture.videoDeviceController.focusControl;
focusControl.focusAsync().then(function () { ..... });
But here it fails with a catastrophic failure in this line. Same, if I remove the .then-part.
I read a bit on the internet and wanted to check my focusSettings to make sure everything is set up fine. But this:
var focusSettings = new Windows.Media.Devices.FocusSettings();
var modeContinuous = Windows.Media.Devices.FocusMode.continuous;
var distanceHyperfocal = Windows.Media.Devices.ManualFocusDistance.hyperfocal;
var rangeFull = Windows.Media.Devices.AutoFocusRange.fullRange;
focusSettings.distance = distanceHyperfocal;
focusSettings.mode = modeContinuous;
focusSettings.autoFocusRange = rangeFull;
and this:
var focusSettings = new Windows.Media.Devices.FocusSettings();
focusSettings.distance = focusControl.supportedFocusDistances[0];
focusSettings.mode = focusControl.supportedFocusModes[0];
focusSettings.autoFocusRange = focusControl.supportedFocusRanges[0];
both crash in this line:
var focusControl = mediaCaptureMgr.videoDeviceController.focusControl;
focusControl.configure(focusSettings);
It says: JavaScript runtime error: The application called an interface that was marshalled for a different thread. But afaik there is no dispatcher in JavaScript so I can't push it to the UI thread (if it is not there already..).
What am I doing wrong?
The default focusSettings sets it to focus once. I wanted to set continuous or at least autofocus everytime before I capture a photo.

JSX duplicate layer Adobe Photoshop

I have a problem with duplicating layers from one document to another. I have this code (.jsx script inside my Photoshop document)
var docRef = app.activeDocument;
app.activeDocument.selection.selectAll();
var calcWidth = app.activeDocument.selection.bounds[2] -app.activeDocument.selection.bounds[0];
var calcHeight = app.activeDocument.selection.bounds[3] - app.activeDocument.selection.bounds[1];
var docResolution = app.activeDocument.resolution;
var document = app.documents.add(calcWidth, calcHeight, docResolution);
app.activeDocument = docRef;
try {
dupObj.artLayers[i].duplicate(document, ElementPlacement.INSIDE);
}
catch(e) {
alert(e)
}
But I am still receiving an error
Error: You can only duplicate layers from the frontmost document.
Have you any ideas how to make it work?
The reason you're getting an error is dupObj is never defined. I think you mean to use docRef, the reference to your source document in line 1. This seems to work fine now:
var docRef = app.activeDocument;
app.activeDocument.selection.selectAll();
var calcWidth = app.activeDocument.selection.bounds[2] -app.activeDocument.selection.bounds[0];
var calcHeight = app.activeDocument.selection.bounds[3] - app.activeDocument.selection.bounds[1];
var docResolution = app.activeDocument.resolution;
var document = app.documents.add(calcWidth, calcHeight, docResolution);
app.activeDocument = docRef;
try {
docRef.artLayers[i].duplicate(document, ElementPlacement.INSIDE); // ** changed to docRef **
}
catch(e) {
alert(e)
}
That being said there might be a few hidden bugs in there you should look at. In this line:
docRef.artLayers[i].duplicate(document, ElementPlacement.INSIDE);
i is never defined, and apparently defaults to 0 without throwing an error. The result is you will only ever duplicate the first layer in the artLayers array.
Also, since you are selecting the entire document using app.activeDocument.selection.selectAll(); there is no need to calculate the size of the selection. It will always be the same size as the original document. You could just use docRef.width and docRef.height as the width and height for the new document. Besides, when you duplicate a layer it will copy the whole layer regardless of the selection, as far as I know.
If you just want to make a new document the same size as the layer you are duplicating try using artLayers[i].bounds instead of selection.bounds
You're not calling the active document: You need to call a reference to the active document and the one your using - hence the error.
var docRef = app.activeDocument;
docRef.selection.selectAll();
var calcWidth = docRef.selection.bounds[2] -app.activeDocument.selection.bounds[0];
var calcHeight = docRef.selection.bounds[3] - app.activeDocument.selection.bounds[1];
var docResolution = docRef.resolution;
var document = app.documents.add(calcWidth, calcHeight, docResolution);
app.activeDocument = docRef;
try {
dupObj.artLayers[i].duplicate(document, ElementPlacement.INSIDE);
}
catch(e) {
alert(e)
}
I've not used dupObj before as I use CS and script listener code for duplicating documents
And I've not checked the code, but give it a go.
The problem is that you're trying to use a variable called document which is reserved in JS.
As Sergey pointed out, document is (amazingly) not a reserved word in JSX because Adobe JSX is not 'regular' JSX
Although it doesn't address the exact syntax error I'll leave this here because it's a quick way to solve the overall problem of copying layers between documents.
// Grab docs
const doc1 = app.activeDocument
const doc2 = app.documents.add(100, 100)
const outputLayer = doc1.layers[0]
const inputLayer = doc2.layers[0]
inputLayer.duplicate(outputLayer, ElementPlacement.INSIDE)

Categories

Resources