I am trying to control the layering of surfaces using zIndex property. It works just fine in Chrome whereas it doesn't in Firefox. After examining the DOM, I observed that the z-index is marked as 0 no matter what we set.
I reproduced the problem in famo.us tutorial code #http://famo.us/university/famous-101/displaying/5/. Replace the tutorial code with the following code. Notice the difference between Chrome and Firefox with the same code.
var Engine = require('famous/core/Engine');
var Surface = require('famous/core/Surface');
var mainContext = Engine.createContext();
var firstSurface = new Surface({
size: [200, 400],
content: 'top',
properties: {
color: 'white',
textAlign: 'center',
backgroundColor: '#FA5C4F',
zIndex: 10
}
});
var secondSurface = new Surface({
size: [300, 200],
content: 'bottom',
properties: {
color: 'white',
textAlign: 'center',
backgroundColor: 'green',
zIndex: 8
}
});
mainContext.add(firstSurface);
mainContext.add(secondSurface);
You should try to avoid using the zIndex property, because Famo.us does Z indexing for you with their 3D render Engine. To achieve what you wish, use a StateModifier and translate your surfaces in the Z direction.
This now works in FireFox as well. Hope it helps!
var Engine = require('famous/core/Engine');
var Surface = require('famous/core/Surface');
var Transform = require('famous/core/Transform');
var StateModifier = require('famous/modifiers/StateModifier');
var mainContext = Engine.createContext();
var firstSurface = new Surface({
size: [200, 400],
content: 'top',
properties: {
color: 'white',
textAlign: 'center',
backgroundColor: '#FA5C4F'
}
});
firstSurface.state = new StateModifier({
transform: Transform.translate(0,0,10)
});
var secondSurface = new Surface({
size: [300, 200],
content: 'bottom',
properties: {
color: 'white',
textAlign: 'center',
backgroundColor: 'green',
}
});
secondSurface.state = new StateModifier({
transform:Transform.translate(0,0,8)
});
mainContext.add(firstSurface.state).add(firstSurface);
mainContext.add(secondSurface.state).add(secondSurface);
Related
I am using the FabricJS graphics library and have added an additional property (name) to a fabric.Rect object. All well and good and it serializes out to JSON correctly.
I am struggling though with the code needed to allow me to subsequently change the customer property once set i.e. to change 'some name' to something else. It is driving me a bit crazy.
Any additional help really appreciated.
Thanks,
Shaun
const o = new fabric.Rect({
width: width,
height: height,
fill: tableFill,
stroke: tableStroke,
strokeWidth: 2,
shadow: tableShadow,
originX: "center",
originY: "center",
centeredRotation: true,
snapAngle: 45,
selectable: true,
strokeUniform: true
});
o.toObject = (function(toObject) {
return function(propertiesToInclude) {
return fabric.util.object.extend(toObject.call(this, propertiesToInclude), {
name: 'some name'
});
};
})(o.toObject);
console.log(o.toObject().name)
So, basically run this code to allow any additional properties to be serialised to and from JSON. In this example a property called name is added.
const originalToObject = fabric.Object.prototype.toObject;
const myAdditional = ['name'];
fabric.Object.prototype.toObject = function (additionalProperties) {
return originalToObject.call(this, myAdditional.concat(additionalProperties));
}
Then create a new Fabric object and set or get the additional properties as needed...
const o = new fabric.Rect({
width: width,
height: height,
fill: tableFill,
stroke: tableStroke,
strokeWidth: 2,
shadow: tableShadow,
originX: "center",
originY: "center",
centeredRotation: true,
snapAngle: 45,
selectable: true,
strokeUniform: true
});
o.name = 'Fred'
console.log(o.toJSON())
I try to add label when user hover element:
var rect = new fabric.Rect({
originX: 'top',
originY: 'top',
width: 150,
height: 120,
fill: 'rgba(255,0,0,0.5)',
transparentCorners: true
});
var text = new fabric.Text('hello world', {
fontSize: 30,
originX: 'top',
originY: 'top'
});
canvas.on('mouse:over', function(e) {
var group = new fabric.Group([ rect, text ], {
left: e.target.left,
top: e.target.top
});
canvas.add(group);
canvas.renderAll();
});
canvas.on('mouse:out', function(e) {
//e.target.set('fill', 'green');
canvas.remove(group);
canvas.renderAll();
});
But when mouse:out fires i get:
Uncaught ReferenceError: group is not defined
at i.<anonymous> (can.js:38)
at i.r (fabric.min.js:1)
at i._fireOverOutEvents (fabric.min.js:3)
at i.findTarget (fabric.min.js:3)
at i.__onMouseMove (fabric.min.js:4)
at i._onMouseMove (fabric.min.js:4)
How can i make a group global with left and top of hovered element, or there is a better way to do that?
group is scoped as a private variable local to the mouse:over event. Try
removing var from:
var group = new fabric.Group([ rect, text ], {
to scope group globally:
group = new fabric.Group([ rect, text ], {
DEMO
var canvas = new fabric.Canvas('c');
var rect = new fabric.Rect({
originX: 'top',
originY: 'top',
width: 150,
height: 120,
fill: 'rgba(255,0,0,0.5)',
});
var text = new fabric.Text('hello world', {
fontSize: 30,
originX: 'top',
originY: 'top'
});
var group = new fabric.Group([ rect, text ], {
left: 0,
top: 0,
selectable : false,
visible: false,
});
canvas.add(group);
canvas.renderAll();
canvas.on('mouse:move', function(e) {
var p = canvas.getPointer(e.e);
group.set({
left: p.x,
top: p.y,
visible: true
});
canvas.renderAll();
});
canvas.on('mouse:out', function(e) {
group.set({
visible: false
})
canvas.renderAll();
});
canvas {
border:2px dotted blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.17/fabric.min.js"></script>
<canvas id='c' width=400 height=400></canvas>
You no need to create group on every mouse over, you just set visible: true/false, so according to this it will visible. Check DEMO.
I am creating a list (ScrollView) with some custom "rows" (The image contains a single "row"). I show 5 rows and have added an event listener so that when the user scrolls to the end, 5 more elements are loaded and displayed. I made this using Alloy but I noticed it was taking too long so I tried writing the views manually.
// created the views programmatically to see if there was any difference from Alloy
function createRow(args) {
var container = Ti.UI.createView({
layout: "vertical",
width: Ti.UI.FILL,
height: "42dp"
});
var rowContent = Ti.UI.createView({
width: Titanium.UI.FILL,
height: "41dp", //Titanium.UI.FILL,
layout: "horizontal",
left: "16dp",
right: "16dp"
});
var border = Ti.UI.createView({
left: "16dp",
right: "16dp",
height: "1dp",
backgroundColor: Colors.darkGrey
});
var titleScroll = Ti.UI.createScrollView({
scrollType: "horizontal",
width: "49%",
horizontalWrap: false
});
var scrollContainer = Ti.UI.createScrollView({
scrollType: "horizontal",
horizontalWrap: false,
width: "50%"
});
var scroll = Ti.UI.createView({
layout: "horizontal",
horizontalWrap: false,
right: 0,
width: Ti.UI.SIZE,
height: Titanium.UI.SIZE
});
var title = Ti.UI.createLabel({
text: args.title,
font: args.isTitle ? {font: "Lato-Regular", fontSize: "22dp"} : {fontFamily: "Lato-Regular", fontSize: "15"},
horizontalWrap: false,
wordWrap: false,
left: 0,
color: Colors.grey,
minimumFontSize: "15dp"
});
if(args.value) {
var t = args.value.join();
scroll.add(Ti.UI.createLabel({
text: t,
color: args.action ? Colors.blue : Colors.black,
font: {fontSize: "15dp", fontFamily: "Lato-Regular"},
right: "5dp",
width: Ti.UI.SIZE,
horizontalWrap: false,
wordWrap: false,
minimumFontSize: "15dp"
}));
}
if(args.data)
scrollContainer.data = args.data; //just a dump of the data used by the click handler
if(args.action)
scrollContainer.addEventListener("click",args.action);
scrollContainer.add(scroll);
titleScroll.add(title);
rowContent.add(titleScroll);
rowContent.add(scrollContainer);
container.add(rowContent);
container.add(border);
return container; //Ti.UI.View
}
function createHeader(args) {
var header = Ti.UI.createView({
layout: "horizontal",
height: "44dp",
backgroundColor: "#fff"
});
var leftView = Ti.UI.createView({
width: "25%",
height: Ti.UI.FILL
});
var rightView = Ti.UI.createView({
width: "25%",
height: Ti.UI.FILL
});
var centerView = Ti.UI.createView({
width: "49%",
height: Ti.UI.FILL
});
var verticalAligner = Ti.UI.createView({
height: Ti.UI.SIZE,
width: Ti.UI.SIZE,
layout: "vertical"
});
var headerTitle = Ti.UI.createLabel({
color: Colors.green,
font: {fontSize: "16.5dp", fontFamily: "Lato-Regular"},
textAlign: "center",
horizontalWrap: false,
wordWrap: false
});
var headerSubtitle = Ti.UI.createLabel({
font: {fontSize: "14dp", fontFamily: "Lato-Regular"},
textAlign: "center",
color: Colors.grey,
horizontalWrap: false,
wordWrap: false
});
if(args.rightView)
rightView.add(args.rightView);
if(args.leftView)
leftView.add(args.leftView);
verticalAligner.add(headerTitle);
verticalAligner.add(headerSubtitle);
centerView.add(verticalAligner);
header.add(leftView);
header.add(centerView);
header.add(rightView);
headerTitle.text = args.title;
headerSubtitle.text = args.subTitle;
return header;
}
function createBlock(args) {
var container = Ti.UI.createView({
layout: "vertical",
width: "100%",
height: Ti.UI.SIZE
});
var covers = Ti.UI.createView({ //
height: "119dp"
});
var content = Ti.UI.createView({
height: Ti.UI.SIZE,
layout: "vertical"
});
function goToEvent() {
Storage.event.id = args.event;
Alloy.Globals.openWin("event");
}
var data = new D.data();
var w = Android ? Ti.Platform.displayCaps.platformWidth : Measure.dpToPX(Ti.Platform.displayCaps.platformWidth);
var h = Measure.dpToPX(119); //Android ? Alloy.Globals.dpToPX(119) : Measure.dpToPX(119);
if(args.images) {
//setTimeout(function() { //timeout didn't make any difference
var image = null;
//for(var i = 0; i < args.images.length; i++) {
image = Ti.UI.createImageView({
image: data.getBlobResized({ //returns a URL for the picture
id: args.images[0], //i
width: w,
height: h
}),
width: iOS ? Measure.pxToDP(w) : Alloy.Globals.pxToDP(w),
height: iOS ? Measure.pxToDP(h): Alloy.Globals.pxToDP(h)
});
image.addEventListener("click",goToEvent);
covers.add(image); //addView
//}
//},0);
}
var row = null;
if(args.rows) {
for(var j=0; j < args.rows.length; j++) {
//row = Alloy.createController("index/events/block/row",args.rows[j]).getView();
content.add(createRow(args.rows[j]));
}
}
container.add(createHeader(args));
container.add(covers);
container.add(content);
return container;
}
In particular, in the code provided, I call 4 times the function createRow() which creates a row inside the element (as seen in the picture). This function takes 7ms to 10ms to run for some reason. So calling it 4 times means it slows the whole process 28-40ms.
On Android the app doesn't lag at all. On iOS it stops user interaction completely until these operations are done
Using latest Titanium SDK (5.2.2GA) on Appcelerator Studio
Testing on iPhone 5, iOS simulator (4s,5,6,6s)
Thank you for your help
In Titanium how do you select an object by the id?
Working with Titanium I created a view with children. I set an event listener on the parent view so I didn't have to create an event listener for each child. In the event listener I determine which child view was clicked by using e.source.id. I need to change the height of the view as it's clicked, and I also need to change the height of previous view that was open. (This is all to show the active view with an underline type of style.)
var selectionView = Ti.UI.createView({
title: 'Selection View',
width: Ti.UI.Fill,
left: 0,
top: 0,
backgroundColor: '#f5f5f5',
layout: 'horizontal',
height: 32,
zIndex: 12
});
var selection_Type = Ti.UI.createView({
title: "Type View",
width: Ti.UI.Fill,
layout: 'horizontal',
backgroundColor: '#ABABAB',
top: 0,
height: 30
});
I can't figure out how to select the object by the id name so I can change it's height.
//storing the id of the last clicked feed label so we can change it's height when it's no longer open
var feedSelector = 'selection_All';
selection_Type.addEventListener('click', function (e) {
Ti.API.info( ' ==== Select Destination Hit ==== ' + e.source.id);
if (e.source.id === feedSelector) {
//refresh the feed view
Ti.API.info('Simulating feed refresh for...' + e.source.id);
}
else {
//reducing active label height to simulatue 2px underline
e.source.setHeight(28);
//reset previous selected label height
//here's the problem
//i know how to do this in regular javascript/html
//but I don't know how to access an object by it's id in Titanium
//setting current label id in feedSelector so we can change it's height next time a button is clicked
feedSelector = e.source.id;
Ti.API.info('Changed value for feedSelector to...' + feedSelector);
}
}
}
);
var selection_All = Ti.UI.createLabel({
id: 'selection_All',
text: 'All',
width: '14.9%',
top: 0,
center: 2,
height: 28,
backgroundColor: '#fff',
textAlign: Titanium.UI.TEXT_ALIGNMENT_CENTER,
color:'#073266',
font:{fontFamily:'Trebuchet MS',fontSize:13}
});
var selection_Status = Ti.UI.createLabel({
id: 'selection_Status',
text: 'Status',
width: '22%',
top: 0,
center: 0,
height: 30,
backgroundColor: '#fff',
textAlign: Titanium.UI.TEXT_ALIGNMENT_CENTER,
color:'#073266',
font:{fontFamily:'Trebuchet MS',fontSize:13}
});
var selection_Article = Ti.UI.createLabel({
id: 'selection_Article',
text: 'Article',
width: '23%',
top: 0,
center: 0,
height: 30,
backgroundColor: '#fff',
textAlign: Titanium.UI.TEXT_ALIGNMENT_CENTER,
color:'#073266',
font:{fontFamily:'Trebuchet MS',fontSize:13}
});
var selection_Video = Ti.UI.createLabel({
id: 'selection_Video',
text: 'Video',
width: '20%',
top: 0,
center: 0,
height: 30,
backgroundColor: '#fff',
textAlign: Titanium.UI.TEXT_ALIGNMENT_CENTER,
color:'#073266',
font:{fontFamily:'Trebuchet MS',fontSize:13}
});
var selection_Audio = Ti.UI.createLabel({
id: 'selection_Audio',
text: 'Audio',
width: '20%',
top: 0,
center: 0,
height: 30,
backgroundColor: '#fff',
textAlign: Titanium.UI.TEXT_ALIGNMENT_CENTER,
color:'#073266',
font:{fontFamily:'Trebuchet MS',fontSize:13}
});
//creating selection type container and adding selection types
selection_Type.add(selection_All);
selection_Type.add(selection_Status);
selection_Type.add(selection_Article);
selection_Type.add(selection_Video);
selection_Type.add(selection_Audio);
selectionView.add(selection_Type);
Edit: I accomplished what I needed to do using a switch statement, but it would be much cleaner if I could get the object by it's id.
var feedSelector = 'selection_All';
selection_Type.addEventListener('click', function (e) {
Ti.API.info( ' ==== Select Destination Hit ==== ' + e.source.id);
if (e.source.id === feedSelector) {
//refresh the feed view if the feed is open and button is clicked
//Ti.API.info('Simulating feed refresh for...' + e.source.id);
alert('Refreshing feed');
}
else {
//reducing active label height to simulatue 2px underline
e.source.setHeight(28);
switch (feedSelector) {
case 'selection_All':
selection_All.setHeight(30);
break;
case 'selection_Status':
selection_Status.setHeight(30);
break;
case 'selection_Article':
selection_Article.setHeight(30);
break;
case 'selection_Video':
selection_Video.setHeight(30);
break;
case 'selection_Audio':
selection_Audio.setHeight(30);
break;
}
feedSelector = e.source.id;
Ti.API.info('Changed value for feedSelector to...' + feedSelector);
}
}
);
If you're using Titanium Studio, just debug on the click event and examine the event object. On the breakpoints stop go to the Expressions tab enter the e event object and examine it. Probably the UI elemnt is there as a property.
Other way around is:
var labelStore = {};
function createLabel(props){
var label = Ti.UI.createLabel(props);
labelStore[props.id] = label;
return label;
}
function getLabelById(id){
return labelStore[id];
}
var selection_Status = createLabel({
id: 'selection_Status',
...
});
and then on click
var id = e.source.id;
var label = getLabelById(id);
....Do what you got to do with the label
I want to code an app that simply puts a rectangle on the screen. But I need to combine kinetic.js and backbone.js for this and i am not sure it can be done.
Kinetic code is:
document.getElementById('rect').addEventListener('click', function() {
rect = new Kinetic.Rect({
x: 239,
y: 75,
width: 100,
height: 50,
fill: 'green',
stroke: 'black',
strokeWidth: 4,
offset: [50,25],
draggable: true,
});
And backbone code
$(function() {
var Shape = Backbone.Model.extend({
defaults: { x:50, y:50, width:150, height:150, color:'gray' },
setTopLeft: function(x,y) { this.set({ x:x, y:y }); },
setDim: function(w,h) { this.set({ width:w, height:h }); },
isCircle: function() { return !!this.get('circle'); }
});
*I added .html file these paths
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.7/jquery.min.js"></script>
<script type="text/javascript" src="http://d3lp1msu2r81bx.cloudfront.net/kjs/js/lib/kinetic-v4.3.3.min.js"></script>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.2.2/underscore-min.js"></script>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/backbone.js/0.5.3/backbone-min.js"></script>
All i want to place kinetic part instead of default values in backbone. Is it possible?
With your help, we wrote this example of work which puts a rectangle on the screen using both kinetic.js and backbone.js. I wish it would be useful for who is looking for this kind of integrated code.
Thanks a lot for your help!
var KineticModel = Backbone.Model.extend({
myRect: null,
createRect : function() {
alert("rectangle created.");
var rect=new Kinetic.Rect({
x: 10,
y: 10,
width: 100,
height: 50,
fill: 'green',
stroke: 'black',
strokeWidth: 1,
offset: [0, 0],
draggable: true,
});
return rect;
}
});
var KineticView = Backbone.View.extend({
tagName: 'span',
stage: null,
layer: null,
initialize: function (options) {
model: options.model;
el: options.el;
this.layer = new Kinetic.Layer();
this.stage = new Kinetic.Stage({ container: this.el, width: 400, height: 400 });
this.stage.add(this.layer);
},
events: {
'click': 'spanClicked'
},
render: function () {
var rect = this.model.createRect();
this.layer.add(rect);
this.stage.add(this.layer);
alert("render");
},
spanClicked: function () {
}
});
var kModel = new KineticModel({});
var kView = new KineticView({ el: '#container', model: kModel });
$('#shapetest').click(function() {
kView.render();
});
Yes this is definitely possible. I would just create a model that stores the data that you will be using in your shape, use a view to render a span tag with click me, attach an event listener to the span and then output the rectangle when the user clicks.
var ShapeModel = Backbone.Model.extend({});
var rectangle = new ShapeModel({
x: 10,
y: 10,
width: 100,
height: 50,
fill: 'green',
stroke: 'black',
strokeWidth: 4,
offset: [0, 0],
draggable: true,
});
var RectangleView = Backbone.View.extend({
tagName: 'span',
initialize: function (options) {
model: options.model;
el: options.el;
},
events: {
'click': 'spanClicked'
},
render: function () {
this.$el.text('click me');
},
spanClicked: function () {
var stage = new Kinetic.Stage({
container: this.el,
width: 200,
height: 200
});
var layer = new Kinetic.Layer();
var rect = new Kinetic.Rect(this.model.toJSON());
layer.add(rect);
stage.add(layer);
}
});
var rectangleView = new RectangleView({ el: '#shapetest', model: rectangle });
rectangleView.render();
I would upgrade to the latest version of Backbone and Underscore too.
Also, thanks for pointing out Kinetic. Hopefully it has support for drawing on the canvas on a mobile device.