0

I am attempting to draw an SVG bezier curve that starts at the end of a text string that is in a Surface. I can set the size of the Surface to [true, true], which is supposed to make the size equal the text bounding box. But if I later try "mySurface.size[0]" in order to get the width, it returns "true"! I need to get a number for the width and height of that bounding box, in order to calculate the end point of my bezier curve! The equivalent DOM approach would just be to use the .getBBox() function.. how do I do this in Famo.us?

seanhalle
  • 973
  • 7
  • 27

4 Answers4

0

this is maybe because the surface hasn't rendered yet. there are a few similar questions here, one of them from me: how can we get the size of a surface within famo.us?

you could also try deferring or using a setTimeout or Engine.nextTick() to check the size on the next loop through.

if you find an elegant solution let us know as this is a big problem in many places using famous - having to do multiple highjinks where you can't really position a scene on the initial setup - you have to let it render and then adjust...

Community
  • 1
  • 1
dcsan
  • 11,333
  • 15
  • 77
  • 118
0

You can use the 'getSize' function and specify 'true' to get the real size of the surface:

var realSize = surface.getSize(true);
IjzerenHein
  • 2,690
  • 1
  • 17
  • 13
0

@ljzerenHein, thanks for the hint.. unfortunately, surface.getSize(true) returns null!
@dcsan, thanks for the link. I believe you may be right, however the solution linked to ends up being much too involved for me.

After much searching, hacking, and experimenting, I've settled on the following approach:
-] use the DOM to get untransformed bounding boxes for text strings
-] format the text strings in SVG form
-] make it so the strings are invisible (set fill and stroke to none)
-] reuse the same "div" element for all the strings that I want to measure
-] once I have the untransformed bounding box, then set the famous surface size to that and then apply modifiers.
-] if I need the bounding box after all transforms have been applied, then get the total accumulated transforms for the surface and multiply that with the original untransformed bounding box

Here's the code to create the DOM element, insert SVG text, then get the bounding box:

//Do this part once, of creating a DOM element and adding it to the document
var el1 =  document.createElement("div");
document.body.appendChild(el1); //only need to append once -- just set innerHTML after

//now set the SVG text string -- from this point down can be repeated for multiple
// strings without removing or re-adding the element, nor fiddling with the DOM
var text1_1_1_SVG = '<svg>  <text x="0" y="0" style="font-family: Arial; font-size: 12;fill:none;stroke:none" id="svgText1">' + myFamousSurface.content + '</text> </svg>';
//note the id is inside the text element! Also the fill and stroke are null so nothing paints
el1.innerHTML = text1_1_1_SVG; 

//now get the element -- this seems to be what triggers the bounding box calc
var test = document.getElementById("svgText1"); //this is ID in the text element

//get the box, take the values out of it, and display them
var rect = test.getBoundingClientRect();
var str = "";
for (i in rect) { //a trick for getting all the attributes of the object
    str += i + " = " + rect[i] + "  ";
}
console.log("svgText1: " + str);

FYI, all of the SVGTextElement methods seem to be callable upon gotElem.
SVGTextElement docs here: http://msdn.microsoft.com/en-us/library/ie/ff972126(v=vs.85).aspx

seanhalle
  • 973
  • 7
  • 27
0

@seanhalle

I'm pretty sure .getSize(true) is returning null because the element has not yet been added to the DOM. Keep in mind that famo.us is synchronized with animation-frames, and updates to the DOM happen don't happen instantly. Accesssing the DOM directly (aka pinging) is strongly disadviced because you will loose the performance benefits that famo.us promises.

What I would do is create a custom view to wrap your surface inside and implement a render-method in it. In the render-method, use getSize(true) to get the size. If it returns null, you know it has not yet been committed to the DOM.

view in action as jsfiddle

define('MyView', function (require, exports, module) {
    var View = require('famous/core/View');
    var Surface = require('famous/core/Surface');
    function MyView() {
        View.apply(this, arguments);
        this.surface = new Surface();
        this.add(this.surface);
    }
    MyView.prototype = Object.create(View.prototype);
    MyView.prototype.constructor = MyView;

    MyView.prototype.render = function() {
        var size = this.getSize(true);
        if (size) {
            if (!this.hasSize) {
                this.hasSize = true;
                console.log('now we have a size: ' + size);
            }
            this.surface.setContent('Size: ' + JSON.stringify(size));
        } else {
            console.log('no size yet');
        }
        return this._node.render();
    };

    module.exports = MyView;
});
IjzerenHein
  • 2,690
  • 1
  • 17
  • 13