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:
setSize
setProportions
setMargins
setAspectRatio
setOrigin
setOpacity
Views
, 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.