Calculate videoHeight relative to videoWidth (aspect-ratio) plus 30px - JavaScript - height

I have a specific video-player which is embedded with an iframe. It has a toolbar at the bottom with a fixed height of 30px.
The video-width is scaled within a bootstrap framework and I want to calculate the height based on the current width.
Like: videoHeight = (videoWidth * 0.5625) + 30px
The value 0.5625 is based on 16*9 aspect-ratio.
Now have I tried the following code:
$(document).ready(function() {
var wide = $('#video-container').css('width');
var calculate = parseInt(wide, 10) * 0.5625;
var videoheight = calculate + 30;
$('#video-container').css('height', videoheight);
});
It works partially, it does calculate the aspect-ratio correctly but it does not add the 30px. Can anybody tell me where I'm going wrong?

Related

Constrain RaphaelJS print to boundaries and position

I am looking for a more efficient way to constrain/set text in Raphael.
I have text that can be written in a box. That text should be centered (based on a number that could change if user wanted to shift text left/right) and the text cannot go beyond the boundaries of the paper.
This is what I do now and its not manageable performance wise
// Build a path
var path = this.paper.print(x, x, text, font, size, 'middle')
// Center line by getting bounding box and shifting to half of that
var bb = path.getBBox()
path.transform('...T' + [-bb.width / 2, 0])
// Compare paper size vs bb
// if it goes beyond I adjust X and Y accordingly and redo the above
So ideally I would like to predict the size of the text before it prints - I am not sure this is possible though as it is probably font dependent. I have looked for a command to contrain text but do not see one?
The other thought I had was to create some kind of shadow paper that does not print to screen and use that to determine size before I render to user. I am not sure where the lag is though - if it's in the screen rendering good but if its in the general logic of creating the svg then that wont help.
I'd appreciate suggestions
You can use opentype.js for measuring text, also you can get svg path by using Path.toPathData method. There is no need of cufon compiled js fonts.
For text measure, create a canvas element somewhere in your DOM.
<canvas id="canvas" style="display: none;"></canvas>
this function will load text and count width, height
function measureText(text, font_name, size) {
var dfd = $.Deferred();
opentype.load('/fonts/' + font_name + '.ttf', function (err, font) {
if (err) {
dfd.reject('Font is not loaded');
console.log('Could not load font: ' + err);
} else {
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, canvas.width, canvas.height);
var tmp_path = font.getPath(text, 100, 100, size);
tmp_path.draw(ctx); // Draw path on the canvas
var ascent = 0;
var descent = 0;
var width = 0;
var scale = 1 / font.unitsPerEm * size;
var glyphs = font.stringToGlyphs(text_string);
for (var i = 0; i < glyphs.length; i++) {
var glyph = glyphs[i];
if (glyph.advanceWidth) {
width += glyph.advanceWidth * scale;
}
if (i < glyphs.length - 1) {
var kerningValue = font.getKerningValue(glyph, glyphs[i + 1]);
width += kerningValue * scale;
}
ascent = Math.max(ascent, glyph.yMax);
descent = Math.min(descent, glyph.yMin);
}
return dfd.resolve({
width: width,
height: ascent * scale,
actualBoundingBoxDescent: descent * scale,
fontBoundingBoxAscent: font.ascender * scale,
fontBoundingBoxDescent: font.descender * scale
});
}
});
return dfd.promise();
},
then use this function to get text dimensions:
$.when(measureText("ABCD", "arial", 48)).done(function(res) {
var height = res.height,
width = res.width;
...
});

Creating reusable objects with Raphael

I'm stuck on a simple problem (I just started using the lib):
What is the most effective way to create sets of objects, replicate them and transform them?
Creating a bunch of circles around a center point:
var height = 150,
width = 150,
branchRadius = 20,
centerX = width / 2,
centerY = height / 2,
treeCrownCenterOffset = 10,
treeCrownRotationCenterX = centerX,
treeCrownRotationCenterY = centerY - treeCrownCenterOffset,
rotationCenter = [treeCrownRotationCenterX , treeCrownRotationCenterY],
paper = Raphael('logo', height , width ),
outerCircle = paper.circle(treeCrownRotationCenterX, treeCrownRotationCenterY, branchRadius).attr({fill:'#F00'}),
innerCircle = paper.circle(treeCrownRotationCenterX, treeCrownRotationCenterY, branchRadius - 4).attr({fill:'#000'}),
branch = paper.set([outerCircle, innerCircle]),
crown = paper.set(),
numCircles = 8, rotationStep = 360 / numCircles, i = 0;
for(; i < numCircles - 1 ; i++) {
//Cloning a branch and pushing it to the crown after transforming it
crown.push(branch.clone().transform('r' + ((i + 1) * rotationStep) + ',' + rotationCenter));
}
//Putting a second crown 200px right of the first crown
//Yes, I'm building a tree :-) No trunk at this point
crown.clone().transform('t200,0');​​​​​
If you like violin, here is the fiddle.
This is my naive code, thinking a set of cloned sets (the crown of cloned branches) will indeed be moved to position (200, 0), next to the first crown.
It doesn't work: looks like a cloned set of cloned elements cannot be moved:
crown.clone().transform('t200,0');​
Not much happens when this line is executed.
Seems like "cloning" is not doing what I expect, and that the transformations are not carried to the second (cloned) collection of objects.
The basic question is:
How does one go about creating reusable objects with Raphael?
Thanks.
You are cloning the set, but since your canvas is only 150px wide, translating it by 200 pixels is sending it off the reservation :)
When you do expand the size of the canvas, however, you will see that only one circle appears to have been cloned. This is not the case. The problem is not with the cloning but the transformation.
I find transformations to be a huge headache. The line "crown.clone().transform('t200,0');​" is applying that transformation to each object in the set, but I believe it is overriding the rotation. Even if it weren't, it would be applying the translation after the rotating, sending the circles scattering as if by centrifugal force.
I know you wanted to avoid looping through the cloned set, but this works:
var crown2 = crown.clone();
for (i = 0; i < crown2.length; i ++) {
crown2[i].transform('t200,0r' + (i * rotationStep) + ',' + rotationCenter);
​}​
Also, note that you didn't add the original branch to the set. You need this:
branch = paper.set([outerCircle, innerCircle]),
crown = paper.set(),
numCircles = 8, rotationStep = 360 / numCircles, i = 0;
//ADD ORIGINAL BRANCH TO SET
crown.push(branch);
Updated fiddle.

raphaeljs: drag and apply transformation to Paper.set()

I started to play a little bit with raphaeljs, however I'm having a small problem when dragging and applying a transformation to a Paper.set()
Here is my example: http://jsfiddle.net/PQZmp/2/
1) Why is the drag event added only to the marker and not the slider?
2) The transformation is supposed to be relative(i.e. translate by and not translate to), however if I drag the marker twice, the second dragging starts from the beginning and not from the end of the first.
EDIT:
After the response of Zero, I created a new JSFiddle example: http://jsfiddle.net/9b9W3/1/
1) It would be cool if this referenced the set instead of the first element of the set. Can't this be done with dragger.apply(slider)? I tried it, but only works on the first execution of the method (perhaps inside Raphael it is already being done but to the first element inside the set instead of the set)
2) According to Raphael docs the transformation should be relative to the object position (i.e. translate by and not translate to). But it is not what is happening according to the jsfiddle above (check both markers drag events).
3) So 2) above creates a third question. If a transform("t30,0") is a translation by 30px horizontally, how is the origin calculated? Based on attr("x") or getBBox().x?
The drag event is actually being added to both the marker and the slider -- but your slider has a stroke-width of 1 and no fill, so unless you catch the exact border, the click "falls through" to the canvas.
Behind that is another issue: the drag is being applied to both elements, but this in your drag handler references a specific element, not the set -- so both elements will drag independently from each other.
Lastly: the reason that each drag is starting from the initial position is because the dx, dy parameters in dragger are relative to the coordinates of the initial drag event, and your transform does not take previous transforms into account. Consider an alternative like this:
var r = new Raphael(0, 0, 400, 200);
var marker = r.path("M10,0L10,100").attr({"stroke-width": 5});
var button = r.rect(0, 0, 20, 20, 1).attr( { 'stroke-width': 2, fill: 'white' } );
var slider = r.set( marker, button );
var startx, starty;
var startDrag = function(){
var bbox = slider.getBBox();
startx = bbox.x;
starty = bbox.y;
console.log(this);
}, dragger = function(dx, dy){
slider.transform("t" + ( startx + dx ) + "," + starty );
}, endDrag = function(){
};
slider.drag(dragger, startDrag, endDrag);
To address your updates:
I believe you can specify the context in which the drag function will be executed as optional fourth, fifth, and six parameters to element.drag. I haven't tried this myself, but it looks like this should work great:
slider.drag( dragger, startDrag, endDrag, slider, slider, slider );
The transformation is relative to the object position. This works great for the first slider because its starting position is 0, but not so great for the second slider because...
...the transformation for min/max sliders should actually be relative to the scale, not the individual markers. Thus you will notice that your max slider (the red one) returns to its initial position just as you drag the mouse cursor back over the zero position. Make sense?
var position;
var rect = paper.rect(20, 20, 40, 40).attr({
cursor: "move",
fill: "#f00",
stroke: "#000"
});
t = paper.text(70,70, 'test').attr({
"font-size":16,
"font-family":
"Arial, Helvetica, sans-serif"
});
var st = paper.set();
st.push(rect, t);
rect.mySet = st;
rect.drag(onMove, onStart, onEnd);
onStart = function () {
positions = new Array();
this.mySet.forEach(function(e) {
var ox = e.attr("x");
var oy = e.attr("y");
positions.push([e, ox, oy]);
});
}
onMove = function (dx, dy) {
for (var i = 0; i < positions.length; i++) {//you can use foreach but I want to
// show that is a simple array
positions[i][0].attr({x: positions[i][1] + dx, y: positions[i][2] + dy});
}
}
onEnd = function() {}

3D rotation of a element in RaphaelJS

I am hoping you might be able to help me determine if the following animation is allowed in raphael.js. I am trying to have an element fly off the page, the idea is to have it appear to fall/fly off in 3D. I am able to tell the element to rotate X degrees and slide off but its lacking the look of the element being independent of hte background. What I would like to do is be able to tell raphael to rotate the top corner "out" as it falls giving the illusion of it falling out of view as picture falling off of a wall. Is this even possible or does Raphael only operate in two dimensional space?
Raphael only deals with 2D space. To implement a 3D flip you have to fake it. Thankfully Raphael implements Scale(sx,sy,x,y) as a transform op, so you can scale about an origin to fake a 3D 'flip' rotation.
For example:
Raphael.el.flipXTransform = function (parentBbox) {
var x = this.getBBox().x;
var width = this.getBBox().width;
parentBbox = parentBbox || { width:width, x: x};
var parentWidth = parentBbox.width;
var parentX = parentBbox.x;
var originX = parentX - x + parentWidth / 2;
return 's-1,1,' + originX + ',0';
};
Raphael.el.flipX = function (duration, easing, parentBbox) {
duration = duration || 500;
easing = easing || 'easeInOut';
var scale = this.flipXTransform(parentBbox);
this.animate({ transform: '...' + scale }, duration, easing);
};
Here's a fiddle example for you to play with. The downside is this doesn't convey a perspective like a true 3D rotate does.

Making charts in RaphaelJS that are 100% width?

I have seen graphs in Flash & stuff that basically adapt nicely to whatever the size of the browser or flexible element they are inside of.... I'm not really too well versed with raphaelJS but can you do this, and if so, how?
In raphaeljs, you can call .setSize on a Raphael object to update its size. To adapt to runtime changes in browser window size, you can respond to a window resize event. Using jQuery, you could do:
// initialize Raphael object
var w = $(window).width(),
h = $(window).height();
var paper = Raphael($("#my_element").get(0), w,h);
$(window).resize(function(){
w = $(window).width();
h = $(window).height();
paper.setSize(w,h);
redraw_element(); // code to handle re-drawing, if necessary
});
This will get you a responsive SVG
var w = 500, h=500;
var paper = Raphael(w,h);
paper.setViewBox(0,0,w,h,true);
paper.setSize('100%', '100%');
Normally you could set the width to %100 and define a viewBox within SVG. But, Raphael JS manually set a width and height directly on your SVG elements, which kills this technique.
Bosh's answer is great, but it was distorting the aspect ratio for me. Had to tweak a few things to get it working correctly. Also, included some logic to maintain a maximum size.
// Variables
var width;
var height;
var maxWidth = 940;
var maxHeight = 600;
var widthPer;
function setSize() {
// Setup width
width = window.innerWidth;
if (width > maxWidth) width = maxWidth;
// Setup height
widthPer = width / maxWidth;
height = widthPer * maxHeight;
}
var paper = Raphael(document.getElementById("infographic"), 940, 600);
paper.setViewBox(0, 0, 940, 600, true);
window.onresize = function(event) {
setSize();
paper.setSize(width,height);
redraw_element();
}