A View is a way to encapsulate a part of the render
tree. Views can be added to the render tree
just like nodes and Surfaces. They can also build their internal render tree with their add method as previously
mentioned. Moreover, they have built-in support for events, options with defaults,
and manage their own size, origin and opacity.
Samsara includes a View constructor class which is used to make custom Views.
It has an extend method which takes a JSON object of custom properties and methods.
A few keys are reserved for special purposes. They are:
| key | description |
|---|---|
defaults |
A JSON object of default options. When a View is instantiated, any options that are not specified are replaced by their default. |
events |
A JSON object of input events, with keys being the name of the event and values being either a function to handle the event, or a string that matches an instance method to handle the event. |
initialize |
A function that is called when the View is instantiated. |
Here's an example of creating a custom View:
var MyView = View.extend({
defaults : {
content : '',
width : 42
},
events : {
hello : "onHello"
},
// Called on instantiation
initialize : function(options){
// Options are passed in after being patched by the defaults
// They are also stored in this.options
var surface = new Surface({
origin : [.5,.5],
content : options.content,
size : [options.width, 100],
properties : {background : 'red'}
});
this.add({align : [.5,.5}).add(surface);
},
onHello : function(data){
console(data.msg);
}
});
This view has default options and listens to a hello event. We can instantiate an instance of this View and
override the default options (if necessary) like so
var myView = new MyView({
content : 'hello'
});
We can send myView a hello event to execute its onHello method:
myView.trigger('hello', {msg : 'hello'}); // hello
Or also call the onHello method direction with myView.onHello(). And lastly, Views can be added to the render
tree just like nodes and Surfaces.
context.add(myView);
Views have an add method for building up their own internal render trees. They accept nodes, Surfaces
or other Views. Here's an example of a View that encapsulates a cross-fade between two Surfaces.
var MyView = View.extend({
initialize : function(){
this.opacity1 = new Transitionable(0);
var opacity2 = this.opacity1.map(function(value){
return 1 - value;
});
var surface1 = new Surface({
opacity : this.opacity1,
properties : {background : 'red'}
});
var surface2 = new Surface({
opacity : opacity2,
properties : {background : 'blue'}
});
this.add(surface1);
this.add(surface2);
},
setCrossfade : function(value, transition, callback){
this.opacity1.set(value, transition, callback);
}
});
See the Pen View Crossfade by SamsaraJS (@samsaraJS) on CodePen.
Views can broadcast and receive events. A View has two EventHandlers, one for input, and
one for output.
All Views have a input property that which listens to incoming events. Views can subscribe
to events, or have events triggered upon them.
var MyView = View.extend({
initialize : function(){
this.input.on('foo', function(data){
console.log(data.msg);
});
}
});
var emitter = new EventEmitter();
myView.subscribe(emitter);
emitter.emit('foo', {msg : 'bar'});
// or
myView.trigger('foo', {msg : 'bar'});
When extending a View, the events dictionary is a shorthand for adding listeners to the input
property (and auto-binds this).
Views also have an emit method for emitting events.
var MyView = View.extend({
sendFoo : function(){
this.emit('foo');
}
});
var myView = new MyView();
myView.sendFoo();
A View's emit method is actually shorthand for view.output.emit. The output property, like a
View's input property, is also an EventHandler.
As we've mentioned, events inbound to the View are received by the input, and events outbound from the View go through the output.
This is particularly useful for thinking of View's themselves as streams. A View can
listen on a Transitionable, or a Samsara input, and can output a stream as well.
var MyView = View.extend({
initialize : function(){
var processedInput = this.input.map(function(value){
return 1 + value;
});
this.output.subscribe(processedInput);
}
});
var myView = new MyView();
var t = new Transitionable(0);
//myView.input is now getting events from the transitionable
myView.subscribe(t);
myView.on('start', function(value){
console.log(value) // 1
});
//myView.output is emitting the processed events
myView.on('end', function(value){
console.log(value) // 2
});
For example, in the SideMenu demo the blue
Content View is getting input from the drag gesture, clamping and normalizing it, and sending that
value to the Drawer and Nav.
Views, like Surfaces can take size, origin and opacity properties on instantiation.
var myView = new MyView({
origin : [.5,.5],
proportions : [.5, .5]
});
context.add({align : [.5,.5]}.add(myView);
Here's an example of instantiating the cross-fade view from before with these size options:
See the Pen View Crossfade Sized by SamsaraJS (@samsaraJS) on CodePen.
Views, like Surfaces also have the same setters for size, origin and opacity:
setSizesetProportionssetMarginssetAspectRatiosetOriginsetOpacityViews, like Surfaces, have a size stream property. In the following example, we render
two Surfaces one on top the other if the View's height is larger than its width, and side
by side if the View's height is smaller than its width.
var MyView = View.extend({
initialize : function(){
var proportions1 = this.size.map(function(size){
return (size[1] > size[0])
? [1, 1/2]
: [1/2, 1];
});
var proportions2 = this.size.map(function(size){
return (size[1] > size[0])
? [1, 1/2]
: [1/2, 1];
});
var surface1 = new Surface({
proportions : proportions1,
properties : {background : 'red'}
});
var surface2 = new Surface({
proportions : proportions2,
properties : {background : 'blue'}
});
var displacement2 = this.size.map(function(size){
return (size[1] > size[0])
? Transform.translateY(size[1]/2)
: Transform.translateX(size[0]/2);
});
this.add(surface1);
this.add({transform : displacement2}).add(surface2);
}
});
Here's a demo, but you'll need to view the demo on Codepen and resize it to see it in action.
See the Pen View Size by SamsaraJS (@samsaraJS) on CodePen.