Skip to content

Instantly share code, notes, and snippets.

@subtleGradient
Last active October 27, 2018 14:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save subtleGradient/423f5e1a7afcdd29417c to your computer and use it in GitHub Desktop.
Save subtleGradient/423f5e1a7afcdd29417c to your computer and use it in GitHub Desktop.
Quick and dirty Framer Layer component for NoFlo
  1. Go to http://app.flowhub.io/
  2. Open or create a project
  3. Create a new component
  4. Paste all this code into the Implementation section
  5. Switch back to the graph editor
  6. Search for this component
  • Note: You can't skip this part! There's a bug in the noflo-ui that makes it impossible to insert a component without searching first. This is beta software.
  1. Insert an instance of this component
  2. Press the play button in the Preview section on the right side of the graph editor UI

You should see a blue square

/**
1. Go to http://app.flowhub.io/
2. Open or create a project
3. Create a new component
4. Paste all this code into the *Implementation* section
5. Switch back to the graph editor
6. Search for this component
* Note: You can't skip this part! There's a bug in the noflo-ui that makes it impossible to insert a component without searching first. This is beta software.
7. Insert an instance of this component
8. Press the play button in the *Preview* section on the right side of the graph editor UI
You should see a blue square
*/
exports.getComponent = getFramerLayerComponent;
var noflo = require('noflo');
function LayerManager(){}
LayerManager.prototype = {
_layer: null,
destroy: function(){
if (this._layer == null) return;
this._layer.destroy();
this._layer = null;
},
findOrCreate: function(callback){
if (this._layer != null) return callback(null, this._layer);
var self = this;
onFramerReady(function(error, Framer){
if (error) return callback(error);
if (self._layer == null && self._name) {
self._layer = Framer.Layer.Layers().filter(function(layer){return layer.name == self._name})[0];
}
if (self._layer == null) self._layer = new Framer.Layer({name:self._name});
self._name = self._layer.name;
callback(null, self._layer);
});
},
set: function(key, value){
if (key == 'name') this._name = value;
if (this._layer == null) return this.findOrCreate(function(error, layer){
layer[key] = value;
});
this._layer[key] = value;
},
subscribeClick: function(callback){
this.findOrCreate(function(error, layer){
layer.on(Framer.Events.Click, function(){
callback();
});
});
},
subscribe: function(event, callback){
this.findOrCreate(function(error, layer){
layer.on(event, callback);
});
},
unsubscribe: function(event, callback){
if (!this._layer) return;
var layer = this._layer;
layer.off(event, callback);
},
}
////////////////////////////////////////////////////////////////////////////////
function getFramerLayerComponent() {
var layerManager = new LayerManager;
function defaultCallback(error){
console.error(error);
// c.outPorts.error.send(error);
}
function setterFor(key){
return function(event, payload){
if (event != 'data') return;
layerManager.set(key, payload);
}
}
var c = new noflo.Component({
inPorts:{
exists: {
datatype: 'boolean',
process: function(event, payload){
if (event != 'data') return;
layerManager[payload ? 'destroy' : 'findOrCreate'](defaultCallback);
},
},
name: { datatype: 'string', process: setterFor('name') },
x: { datatype: 'number', process: setterFor('x') },
y: { datatype: 'number', process: setterFor('y') },
width: { datatype: 'number', process: setterFor('width') },
height: { datatype: 'number', process: setterFor('height') },
origin_x: { datatype: 'number', process: setterFor('originX') },
origin_y: { datatype: 'number', process: setterFor('originY') },
rotation_x: { datatype: 'number', process: setterFor('rotationX') },
rotation_y: { datatype: 'number', process: setterFor('rotationY') },
rotation_z: { datatype: 'number', process: setterFor('rotationZ') },
scale_x: { datatype: 'number', process: setterFor('scaleX') },
scale_y: { datatype: 'number', process: setterFor('scaleY') },
scale_z: { datatype: 'number', process: setterFor('scaleZ') },
visible: { datatype: 'boolean', process: setterFor('visible') },
opacity: { datatype: 'number', process: setterFor('opacity') },
clip: { datatype: 'boolean', process: setterFor('clip') },
},
outPorts: {
// error:{datatype:'object'},
clicked:{datatype:'boolean'},
touching:{datatype:'boolean'},
}
});
c.outPorts.clicked.once('attach', function(){
c.outPorts.clicked.send(false);
layerManager.subscribeClick(function(){
c.outPorts.clicked.send(true);
setTimeout(function(){
c.outPorts.clicked.send(false);
}, 100);
});
});
function onTouchStart(){
c.outPorts.touching.send(true);
}
function onTouchEnd(){
c.outPorts.touching.send(false);
}
c.outPorts.touching.on('attach', function(){
onTouchEnd();
onFramerReady(function(error, Framer){
if (error) return console.error(error);
layerManager.subscribe(Framer.Events.TouchStart, onTouchStart);
layerManager.subscribe(Framer.Events.TouchEnd, onTouchEnd);
});
c.outPorts.touching.on('detach', function(){
if (c.outPorts.touching.isAttached()) return;
layerManager.unsubscribe(Framer.Events.TouchStart, onTouchStart);
layerManager.unsubscribe(Framer.Events.TouchEnd, onTouchEnd);
});
});
return c;
}
////////////////////////////////////////////////////////////////////////////////
function loadScript(src){
var firstScript = document.getElementsByTagName('script')[0];
var firstScriptParent = firstScript.parentNode;
var newScript = document.createElement('script');
newScript.src = src;
firstScriptParent.insertBefore(newScript, firstScript);
}
onFramerReady.URI = 'http://builds.framerjs.com/293c7a7/framer.js';
onFramerReady.TIMEOUT = 50;
onFramerReady.MAX_ATTEMPTS = 5000 / onFramerReady.TIMEOUT;
function onFramerReady(callback){
if (typeof Framer == 'object' && typeof Layer == 'function'){
onFramerReady = function(callback){ callback(null, Framer) };
callback(null, Framer);
return;
}
if (!onFramerReady.isTrying) {
loadScript(onFramerReady.URI);
onFramerReady.isTrying = true;
}
callback._onFramerReady_attempts = (callback._onFramerReady_attempts || 0) + 1;
if (callback._onFramerReady_attempts > onFramerReady.MAX_ATTEMPTS){
callback(Error("Looks like Framer isn't loading (_onFramerReady_attempts exceeded MAX_ATTEMPTS while waiting for '" + onFramerReady.URI + "' to load)"));
return;
}
setTimeout(function(){
onFramerReady(callback);
}, onFramerReady.TIMEOUT);
}
@tayro
Copy link

tayro commented May 14, 2014

Make sure "Visible" is set to True on the patch inputs.

@subtleGradient
Copy link
Author

Maybe I should make that the default ;)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment