UILoader cover background in ActionScript 3 - actionscript-3

I'm using ActionScript 3 and the UILoader component to show a .jpg image.
I would like a "covered background" result, like the css property :
background-size : cover;
I tested the parameter scaleContent (true or false):
uilBackground.scaleContent = true;
But the result is not a true "covered background".
I think to set the scaleContent false and made a ratio calcul to modify the size of the UILoader component.
Is it a good way ? Are there others solutions ?
Thanks.
Julien

Based on the Alsacrations jQuery solution (http://www.alsacreations.com/astuce/lire/1216-arriere-plan-background-extensible.html) :
function backgroundCover(){
var image_width:Number = uilBackground.content.width;
var image_height:Number = uilBackground.content.height;
var over = image_width / image_height;
var under = image_height / image_width;
var body_width = uilBackground.width;
var body_height = uilBackground.height;
if (body_width / body_height >= over) {
uilBackground.content.width = body_width;
uilBackground.content.height = Math.ceil(under * body_width);
uilBackground.move(0,(Math.abs((under * body_width) - body_height) / -2));
}else{
uilBackground.content.width = Math.ceil(over * body_height);
uilBackground.content.height = body_height;
uilBackground.move((Math.abs((over * body_height) - body_width) / -2),0);
}
//trace(uilBackground.content.width);
//trace(uilBackground.content.height);
}
It seems work.

Related

Dynamically change font-size within parent element with no overflow [duplicate]

I need to display user entered text into a fixed size div. What i want is for the font size to be automatically adjusted so that the text fills the box as much as possible.
So - If the div is 400px x 300px. If someone enters ABC then it's really big font. If they enter a paragraph, then it would be a tiny font.
I'd probably want to start with a maximum font size - maybe 32px, and while the text is too big to fit the container, shrink the font size until it fits.
This is what I ended up with:
Here is a link to the plugin: https://plugins.jquery.com/textfill/
And a link to the source: http://jquery-textfill.github.io/
;(function($) {
$.fn.textfill = function(options) {
var fontSize = options.maxFontPixels;
var ourText = $('span:visible:first', this);
var maxHeight = $(this).height();
var maxWidth = $(this).width();
var textHeight;
var textWidth;
do {
ourText.css('font-size', fontSize);
textHeight = ourText.height();
textWidth = ourText.width();
fontSize = fontSize - 1;
} while ((textHeight > maxHeight || textWidth > maxWidth) && fontSize > 3);
return this;
}
})(jQuery);
$(document).ready(function() {
$('.jtextfill').textfill({ maxFontPixels: 36 });
});
and my HTML is like this
<div class='jtextfill' style='width:100px;height:50px;'>
<span>My Text Here</span>
</div>
I didn't find any of the previous solutions to be adequate enough due to bad performance, so I made my own that uses simple math instead of looping. Should work fine in all browsers as well.
According to this performance test case it is much faster then the other solutions found here.
(function($) {
$.fn.textfill = function(maxFontSize) {
maxFontSize = parseInt(maxFontSize, 10);
return this.each(function(){
var ourText = $("span", this),
parent = ourText.parent(),
maxHeight = parent.height(),
maxWidth = parent.width(),
fontSize = parseInt(ourText.css("fontSize"), 10),
multiplier = maxWidth/ourText.width(),
newSize = (fontSize*(multiplier-0.1));
ourText.css(
"fontSize",
(maxFontSize > 0 && newSize > maxFontSize) ?
maxFontSize :
newSize
);
});
};
})(jQuery);
If you want to contribute I've added this to Gist.
As much as I love the occasional upvotes I get for this answer (thanks!), this is really not the greatest approach to this problem. Please check out some of the other wonderful answers here, especially the ones that have found solutions without looping.
Still, for the sake of reference, here's my original answer:
<html>
<head>
<style type="text/css">
#dynamicDiv
{
background: #CCCCCC;
width: 300px;
height: 100px;
font-size: 64px;
overflow: hidden;
}
</style>
<script type="text/javascript">
function shrink()
{
var textSpan = document.getElementById("dynamicSpan");
var textDiv = document.getElementById("dynamicDiv");
textSpan.style.fontSize = 64;
while(textSpan.offsetHeight > textDiv.offsetHeight)
{
textSpan.style.fontSize = parseInt(textSpan.style.fontSize) - 1;
}
}
</script>
</head>
<body onload="shrink()">
<div id="dynamicDiv"><span id="dynamicSpan">DYNAMIC FONT</span></div>
</body>
</html>
And here's a version with classes:
<html>
<head>
<style type="text/css">
.dynamicDiv
{
background: #CCCCCC;
width: 300px;
height: 100px;
font-size: 64px;
overflow: hidden;
}
</style>
<script type="text/javascript">
function shrink()
{
var textDivs = document.getElementsByClassName("dynamicDiv");
var textDivsLength = textDivs.length;
// Loop through all of the dynamic divs on the page
for(var i=0; i<textDivsLength; i++) {
var textDiv = textDivs[i];
// Loop through all of the dynamic spans within the div
var textSpan = textDiv.getElementsByClassName("dynamicSpan")[0];
// Use the same looping logic as before
textSpan.style.fontSize = 64;
while(textSpan.offsetHeight > textDiv.offsetHeight)
{
textSpan.style.fontSize = parseInt(textSpan.style.fontSize) - 1;
}
}
}
</script>
</head>
<body onload="shrink()">
<div class="dynamicDiv"><span class="dynamicSpan">DYNAMIC FONT</span></div>
<div class="dynamicDiv"><span class="dynamicSpan">ANOTHER DYNAMIC FONT</span></div>
<div class="dynamicDiv"><span class="dynamicSpan">AND YET ANOTHER DYNAMIC FONT</span></div>
</body>
</html>
Most of the other answers use a loop to reduce the font-size until it fits on the div, this is VERY slow since the page needs to re-render the element each time the font changes size. I eventually had to write my own algorithm to make it perform in a way that allowed me to update its contents periodically without freezing the user browser. I added some other functionality (rotating text, adding padding) and packaged it as a jQuery plugin, you can get it at:
https://github.com/DanielHoffmann/jquery-bigtext
simply call
$("#text").bigText();
and it will fit nicely on your container.
See it in action here:
http://danielhoffmann.github.io/jquery-bigtext/
For now it has some limitations, the div must have a fixed height and width and it does not support wrapping text into multiple lines.
I will work on getting an option to set the maximum font-size.
Edit: I have found some more problems with the plugin, it does not handle other box-model besides the standard one and the div can't have margins or borders. I will work on it.
Edit2: I have now fixed those problems and limitations and added more options. You can set maximum font-size and you can also choose to limit the font-size using either width, height or both. I will work into accepting a max-width and max-height values in the wrapper element.
Edit3: I have updated the plugin to version 1.2.0. Major cleanup on the code and new options (verticalAlign, horizontalAlign, textAlign) and support for inner elements inside the span tag (like line-breaks or font-awesome icons.)
This is based on what GeekyMonkey posted above, with some modifications.
; (function($) {
/**
* Resize inner element to fit the outer element
* #author Some modifications by Sandstrom
* #author Code based on earlier works by Russ Painter (WebDesign#GeekyMonkey.com)
* #version 0.2
*/
$.fn.textfill = function(options) {
options = jQuery.extend({
maxFontSize: null,
minFontSize: 8,
step: 1
}, options);
return this.each(function() {
var innerElements = $(this).children(':visible'),
fontSize = options.maxFontSize || innerElements.css("font-size"), // use current font-size by default
maxHeight = $(this).height(),
maxWidth = $(this).width(),
innerHeight,
innerWidth;
do {
innerElements.css('font-size', fontSize);
// use the combined height of all children, eg. multiple <p> elements.
innerHeight = $.map(innerElements, function(e) {
return $(e).outerHeight();
}).reduce(function(p, c) {
return p + c;
}, 0);
innerWidth = innerElements.outerWidth(); // assumes that all inner elements have the same width
fontSize = fontSize - options.step;
} while ((innerHeight > maxHeight || innerWidth > maxWidth) && fontSize > options.minFontSize);
});
};
})(jQuery);
Here's an improved looping method that uses binary search to find the largest possible size that fits into the parent in the fewest steps possible (this is faster and more accurate than stepping by a fixed font size). The code is also optimized in several ways for performance.
By default, 10 binary search steps will be performed, which will get within 0.1% of the optimal size. You could instead set numIter to some value N to get within 1/2^N of the optimal size.
Call it with a CSS selector, e.g.: fitToParent('.title-span');
/**
* Fit all elements matching a given CSS selector to their parent elements'
* width and height, by adjusting the font-size attribute to be as large as
* possible. Uses binary search.
*/
var fitToParent = function(selector) {
var numIter = 10; // Number of binary search iterations
var regexp = /\d+(\.\d+)?/;
var fontSize = function(elem) {
var match = elem.css('font-size').match(regexp);
var size = match == null ? 16 : parseFloat(match[0]);
return isNaN(size) ? 16 : size;
}
$(selector).each(function() {
var elem = $(this);
var parentWidth = elem.parent().width();
var parentHeight = elem.parent().height();
if (elem.width() > parentWidth || elem.height() > parentHeight) {
var maxSize = fontSize(elem), minSize = 0.1;
for (var i = 0; i < numIter; i++) {
var currSize = (minSize + maxSize) / 2;
elem.css('font-size', currSize);
if (elem.width() > parentWidth || elem.height() > parentHeight) {
maxSize = currSize;
} else {
minSize = currSize;
}
}
elem.css('font-size', minSize);
}
});
};
I've created a directive for AngularJS - heavely inspired by GeekyMonkey's answer but without the jQuery dependency.
Demo: http://plnkr.co/edit/8tPCZIjvO3VSApSeTtYr?p=preview
Markup
<div class="fittext" max-font-size="50" text="Your text goes here..."></div>
Directive
app.directive('fittext', function() {
return {
scope: {
minFontSize: '#',
maxFontSize: '#',
text: '='
},
restrict: 'C',
transclude: true,
template: '<div ng-transclude class="textContainer" ng-bind="text"></div>',
controller: function($scope, $element, $attrs) {
var fontSize = $scope.maxFontSize || 50;
var minFontSize = $scope.minFontSize || 8;
// text container
var textContainer = $element[0].querySelector('.textContainer');
angular.element(textContainer).css('word-wrap', 'break-word');
// max dimensions for text container
var maxHeight = $element[0].offsetHeight;
var maxWidth = $element[0].offsetWidth;
var textContainerHeight;
var textContainerWidth;
var resizeText = function(){
do {
// set new font size and determine resulting dimensions
textContainer.style.fontSize = fontSize + 'px';
textContainerHeight = textContainer.offsetHeight;
textContainerWidth = textContainer.offsetWidth;
// shrink font size
var ratioHeight = Math.floor(textContainerHeight / maxHeight);
var ratioWidth = Math.floor(textContainerWidth / maxWidth);
var shrinkFactor = ratioHeight > ratioWidth ? ratioHeight : ratioWidth;
fontSize -= shrinkFactor;
} while ((textContainerHeight > maxHeight || textContainerWidth > maxWidth) && fontSize > minFontSize);
};
// watch for changes to text
$scope.$watch('text', function(newText, oldText){
if(newText === undefined) return;
// text was deleted
if(oldText !== undefined && newText.length < oldText.length){
fontSize = $scope.maxFontSize;
}
resizeText();
});
}
};
});
I forked the script above from Marcus Ekwall: https://gist.github.com/3945316 and tweaked it to my preferences, it now fires when the window is resized, so that the child always fits its container. I've pasted the script below for reference.
(function($) {
$.fn.textfill = function(maxFontSize) {
maxFontSize = parseInt(maxFontSize, 10);
return this.each(function(){
var ourText = $("span", this);
function resizefont(){
var parent = ourText.parent(),
maxHeight = parent.height(),
maxWidth = parent.width(),
fontSize = parseInt(ourText.css("fontSize"), 10),
multiplier = maxWidth/ourText.width(),
newSize = (fontSize*(multiplier));
ourText.css("fontSize", maxFontSize > 0 && newSize > maxFontSize ? maxFontSize : newSize );
}
$(window).resize(function(){
resizefont();
});
resizefont();
});
};
})(jQuery);
Here's my modification of the OP's answer.
In short, many people who tried to optimize this complained that a loop was being used. Yes, while loops can be slow, other approaches can be inaccurate.
Therefore, my approach uses Binary Search to find the best Font Size:
$.fn.textfill = function()
{
var self = $(this);
var parent = self.parent();
var attr = self.attr('max-font-size');
var maxFontSize = parseInt(attr, 10);
var unit = attr.replace(maxFontSize, "");
var minFontSize = parseInt(self.attr('min-font-size').replace(unit, ""));
var fontSize = (maxFontSize + minFontSize) / 2;
var maxHeight = parent.height();
var maxWidth = parent.width();
var textHeight;
var textWidth;
do
{
self.css('font-size', fontSize + unit);
textHeight = self.height();
textWidth = self.width();
if(textHeight > maxHeight || textWidth > maxWidth)
{
maxFontSize = fontSize;
fontSize = Math.floor((fontSize + minFontSize) / 2);
}
else if(textHeight < maxHeight || textWidth < maxWidth)
{
minFontSize = fontSize;
fontSize = Math.floor((fontSize + maxFontSize) / 2);
}
else
break;
}
while(maxFontSize - minFontSize > 1 && maxFontSize > minFontSize);
self.css('font-size', fontSize + unit);
return this;
}
function resizeText()
{
$(".textfill").textfill();
}
$(document).ready(resizeText);
$(window).resize(resizeText);
This also allows the element to specify the minimum and maximum font:
<div class="container">
<div class="textfill" min-font-size="10px" max-font-size="72px">
Text that will fill the container, to the best of its abilities, and it will <i>never</i> have overflow.
</div>
</div>
Furthermore, this algorithm is unitless. You may specify em, rem, %, etc. and it will use that for its final result.
Here's the Fiddle: https://jsfiddle.net/fkhqhnqe/1/
I had exactly the same problem with my website. I have a page that is displayed on a projector, on walls, big screens..
As I don't know the max size of my font, I re-used the plugin above of #GeekMonkey but incrementing the fontsize :
$.fn.textfill = function(options) {
var defaults = { innerTag: 'span', padding: '10' };
var Opts = jQuery.extend(defaults, options);
return this.each(function() {
var ourText = $(Opts.innerTag + ':visible:first', this);
var fontSize = parseFloat(ourText.css('font-size'),10);
var doNotTrepass = $(this).height()-2*Opts.padding ;
var textHeight;
do {
ourText.css('font-size', fontSize);
textHeight = ourText.height();
fontSize = fontSize + 2;
} while (textHeight < doNotTrepass );
});
};
The proposed iterative solutions can be sped up dramatically on two fronts:
1) Multiply the font size by some constant, rather than adding or subtracting 1.
2) First, zero in using a course constant, say, double the size each loop. Then, with a rough idea of where to start, do the same thing with a finer adjustment, say, multiply by 1.1. While the perfectionist might want the exact integer pixel size of the ideal font, most observers don't notice the difference between 100 and 110 pixels. If you are a perfectionist, then repeat a third time with an even finer adjustment.
Rather than writing a specific routine or plug-in that answers the exact question, I just rely on the basic ideas and write variations of the code to handle all kinds of layout issues, not just text, including fitting divs, spans, images,... by width, height, area,... within a container, matching another element....
Here's an example:
var nWindowH_px = jQuery(window).height();
var nWas = 0;
var nTry = 5;
do{
nWas = nTry;
nTry *= 2;
jQuery('#divTitle').css('font-size' ,nTry +'px');
}while( jQuery('#divTitle').height() < nWindowH_px );
nTry = nWas;
do{
nWas = nTry;
nTry = Math.floor( nTry * 1.1 );
jQuery('#divTitle').css('font-size' ,nTry +'px');
}while( nWas != nTry && jQuery('#divTitle').height() < nWindowH_px );
jQuery('#divTitle').css('font-size' ,nWas +'px');
Here is a version of the accepted answer which can also take a minFontSize parameter.
(function($) {
/**
* Resizes an inner element's font so that the inner element completely fills the outer element.
* #author Russ Painter WebDesign#GeekyMonkey.com
* #author Blake Robertson
* #version 0.2 -- Modified it so a min font parameter can be specified.
*
* #param {Object} Options which are maxFontPixels (default=40), innerTag (default='span')
* #return All outer elements processed
* #example <div class='mybigdiv filltext'><span>My Text To Resize</span></div>
*/
$.fn.textfill = function(options) {
var defaults = {
maxFontPixels: 40,
minFontPixels: 10,
innerTag: 'span'
};
var Opts = jQuery.extend(defaults, options);
return this.each(function() {
var fontSize = Opts.maxFontPixels;
var ourText = $(Opts.innerTag + ':visible:first', this);
var maxHeight = $(this).height();
var maxWidth = $(this).width();
var textHeight;
var textWidth;
do {
ourText.css('font-size', fontSize);
textHeight = ourText.height();
textWidth = ourText.width();
fontSize = fontSize - 1;
} while ((textHeight > maxHeight || textWidth > maxWidth) && fontSize > Opts.minFontPixels);
});
};
})(jQuery);
You can use FitText.js (github page) to solve this problem. Is really small and efficient compared to TextFill. TextFill uses an expensive while loop and FitText don't.
Also FitText is more flexible (I use it in a proyect with very special requirements and works like a champ!).
HTML:
<div class="container">
<h1 id="responsive_headline">Your fancy title</h1>
</div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script src="jquery.fittext.js"></script>
<script>
jQuery("#responsive_headline").fitText();
</script>
You also can set options to it:
<script>
jQuery("#responsive_headline").fitText(1, { minFontSize: '30px', maxFontSize: '90px'});
</script>
CSS:
#responsive_headline {
width: 100%;
display: block;
}
And if you need it, FitText also has a no-jQuery version.
EDIT: This code was used to show notes on top of a HTML5 video. It changes the font-size on the fly when the video is resized (when the browser window is resized.) The notes was connected to the video (just like notes on YouTube), which is why the code uses instances instead of a DOM handle directly.
As per request I'll throw in some code that I used to achieve this. (Text boxes over an HTML5 video.) The code was written a long time ago, and I quite frankly think it's pretty messy. Since the question is already answered and an answer is already accepted a long time ago I don't bother rewriting this. But if anyone wants to simplify this a bit, you're more than welcome!
// Figure out the text size:
var text = val['text'];
var letters = text.length;
var findMultiplier = function(x) { // g(x)
/* By analysing some functions with regression, the resulting function that
gives the best font size with respect to the number of letters and the size
of the note is:
g(x) = 8.3 - 2.75x^0.15 [1 < x < 255]
f(x) = g(letters) * (x / 1000)^0.5
Font size = f(size)
*/
return 8.3 - 2.75 * Math.pow(x, 0.15);
};
var findFontSize = function(x) { // f(x)
return findMultiplier(letters) * Math.pow(x / 1000, 0.5);
};
val.setFontSizeListener = function() {
p.style.fontSize = '1px'; // So the text should not overflow the box when measuring.
var noteStyle = window.getComputedStyle(table);
var width = noteStyle.getPropertyValue('width');
var height = noteStyle.getPropertyValue('height');
var size = width.substring(0, width.length - 2) * height.substring(0, height.length - 2);
p.style.fontSize = findFontSize(size) + 'px';
};
window.addEventListener('resize', val.setFontSizeListener);
You'll probably need to tweak these numbers from font-family to font-family. A good way to do this is download a free graph visualizer called GeoGebra. Change the length of the text and the size of the box. Then you manually set the size. Plot the manual results into the coordinate system. Then you enter the two equations I've posted here and you tweak the numbers until "my" graph fits your own manually plotted points.
Just wanted to add my version for contenteditables.
$.fn.fitInText = function() {
this.each(function() {
let textbox = $(this);
let textboxNode = this;
let mutationCallback = function(mutationsList, observer) {
if (observer) {
observer.disconnect();
}
textbox.css('font-size', 0);
let desiredHeight = textbox.css('height');
for (i = 12; i < 50; i++) {
textbox.css('font-size', i);
if (textbox.css('height') > desiredHeight) {
textbox.css('font-size', i - 1);
break;
}
}
var config = {
attributes: true,
childList: true,
subtree: true,
characterData: true
};
let newobserver = new MutationObserver(mutationCallback);
newobserver.observe(textboxNode, config);
};
mutationCallback();
});
}
$('#inner').fitInText();
#outer {
display: table;
width: 100%;
}
#inner {
border: 1px solid black;
height: 170px;
text-align: center;
display: table-cell;
vertical-align: middle;
word-break: break-all;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="outer">
<div id="inner" contenteditable=true>
TEST
</div>
</div>
This uses binary search, doing 10 iterations. The naive way was to do a while loop and increase the font size by 1 until the element started to overflow. You can determine when an element begins to overflow using element.offsetHeight and element.scrollHeight. If scrollHeight is bigger than offsetHeight, you have a font size that is too big.
Binary search is a much better algorithm for this. It also is limited by the number of iterations you want to perform. Simply call flexFont and insert the div id and it will adjust the font size between 8px and 96px.
I have spent some time researching this topic and trying different libraries, but ultimately I think this is the easiest and most straightforward solution that will actually work.
Note if you want you can change to use offsetWidth and scrollWidth, or add both to this function.
// Set the font size using overflow property and div height
function flexFont(divId) {
var content = document.getElementById(divId);
content.style.fontSize = determineMaxFontSize(content, 8, 96, 10, 0) + "px";
};
// Use binary search to determine font size
function determineMaxFontSize(content, min, max, iterations, lastSizeNotTooBig) {
if (iterations === 0) {
return lastSizeNotTooBig;
}
var obj = fontSizeTooBig(content, min, lastSizeNotTooBig);
// if `min` too big {....min.....max.....}
// search between (avg(min, lastSizeTooSmall)), min)
// if `min` too small, search between (avg(min,max), max)
// keep track of iterations, and the last font size that was not too big
if (obj.tooBig) {
(lastSizeTooSmall === -1) ?
determineMaxFontSize(content, min / 2, min, iterations - 1, obj.lastSizeNotTooBig, lastSizeTooSmall) :
determineMaxFontSize(content, (min + lastSizeTooSmall) / 2, min, iterations - 1, obj.lastSizeNotTooBig, lastSizeTooSmall);
} else {
determineMaxFontSize(content, (min + max) / 2, max, iterations - 1, obj.lastSizeNotTooBig, min);
}
}
// determine if fontSize is too big based on scrollHeight and offsetHeight,
// keep track of last value that did not overflow
function fontSizeTooBig(content, fontSize, lastSizeNotTooBig) {
content.style.fontSize = fontSize + "px";
var tooBig = content.scrollHeight > content.offsetHeight;
return {
tooBig: tooBig,
lastSizeNotTooBig: tooBig ? lastSizeNotTooBig : fontSize
};
}
I got the same problem and the solution is basically use javascript to control font-size.
Check this example on codepen:
https://codepen.io/ThePostModernPlatonic/pen/BZKzVR
This is example is only for height, maybe you need to put some if's about the width.
try to resize it
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Documento sem título</title>
<style>
</style>
</head>
<body>
<div style="height:100vh;background-color: tomato;" id="wrap">
<h1 class="quote" id="quotee" style="padding-top: 56px">Because too much "light" doesn't <em>illuminate</em> our paths and warm us, it only blinds and burns us.</h1>
</div>
</body>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script>
var multiplexador = 3;
initial_div_height = document.getElementById ("wrap").scrollHeight;
setInterval(function(){
var div = document.getElementById ("wrap");
var frase = document.getElementById ("quotee");
var message = "WIDTH div " + div.scrollWidth + "px. "+ frase.scrollWidth+"px. frase \n";
message += "HEIGHT div " + initial_div_height + "px. "+ frase.scrollHeight+"px. frase \n";
if (frase.scrollHeight < initial_div_height - 30){
multiplexador += 1;
$("#quotee").css("font-size", multiplexador);
}
console.log(message);
}, 10);
</script>
</html>
I did like
let name = "Making statements based on opinion; back them up with references or personal experience."
let originFontSize = 15;
let maxDisplayCharInLine = 50;
let fontSize = Math.min(originFontSize, originFontSize / (name.length / maxDisplayCharInLine));
I have found a way to prevent the use of loops to shrink the text. It adjusts the font-size by multiplying it for the rate between container's width and content width. So if the container's width is 1/3 of the content, the font-size will be reduced by 1/3 and will container's width. To scale up, I have used a while loop, until content is bigger than container.
function fitText(outputSelector){
// max font size in pixels
const maxFontSize = 50;
// get the DOM output element by its selector
let outputDiv = document.getElementById(outputSelector);
// get element's width
let width = outputDiv.clientWidth;
// get content's width
let contentWidth = outputDiv.scrollWidth;
// get fontSize
let fontSize = parseInt(window.getComputedStyle(outputDiv, null).getPropertyValue('font-size'),10);
// if content's width is bigger than elements width - overflow
if (contentWidth > width){
fontSize = Math.ceil(fontSize * width/contentWidth,10);
fontSize = fontSize > maxFontSize ? fontSize = maxFontSize : fontSize - 1;
outputDiv.style.fontSize = fontSize+'px';
}else{
// content is smaller than width... let's resize in 1 px until it fits
while (contentWidth === width && fontSize < maxFontSize){
fontSize = Math.ceil(fontSize) + 1;
fontSize = fontSize > maxFontSize ? fontSize = maxFontSize : fontSize;
outputDiv.style.fontSize = fontSize+'px';
// update widths
width = outputDiv.clientWidth;
contentWidth = outputDiv.scrollWidth;
if (contentWidth > width){
outputDiv.style.fontSize = fontSize-1+'px';
}
}
}
}
This code is part of a test that I have uploaded to Github https://github.com/ricardobrg/fitText/
I went with geekMonkey solution, but it's too slow. What he does, is he adjusts the font size to maximum (maxFontPixels) and then checks if it fits inside the container. else it reduces the font size by 1px and checks again. Why not simply check the previous container for the height and submit that value? (yes, I know why, but I now made a solution, that only works on the height and also has a min/max option)
Here is a much quicker solution:
var index_letters_resize;
(index_letters_resize = function() {
$(".textfill").each(function() {
var
$this = $(this),
height = Math.min( Math.max( parseInt( $this.height() ), 40 ), 150 );
$this.find(".size-adjust").css({
fontSize: height
});
});
}).call();
$(window).on('resize', function() {
index_letters_resize();
);
and this would be the HTML:
<div class="textfill">
<span class="size-adjust">adjusted element</span>
other variable stuff that defines the container size
</div>
Again: this solution ONLY checks for the height of the container. That's why this function does not has to check, if the element fits inside. But I also implemented a min/max value (40min, 150max) so for me this works perfectly fine (and also works on window resize).
Here is another version of this solution:
shrinkTextInElement : function(el, minFontSizePx) {
if(!minFontSizePx) {
minFontSizePx = 5;
}
while(el.offsetWidth > el.parentNode.offsetWidth || el.offsetHeight > el.parentNode.offsetHeight) {
var newFontSize = (parseInt(el.style.fontSize, 10) - 3);
if(newFontSize <= minFontSizePx) {
break;
}
el.style.fontSize = newFontSize + "px";
}
}

How to change an image every 15 seconds in HTML

I have an image in my HTML page, and I would like it to change to a different image every 15 seconds.
<img src="img/img 1.jpg" alt="image">
In my local folder img, I have two images which are img 1.jpg and img 2.jpg. How do I change the img 1.jpg to img 2.jpg after 15 seconds?
Try it:
$(document).ready(function(){
var img = 0;
var slides = new Array();
while (img < 5) {
img++;
// put your image src in sequence
var src = 'assets/images/earth/Sequence' + img + '.jpg';
slides.push(src);
}
var index = 0,timer = 0;
showNextSlide();
timer = setInterval(showNextSlide, 15000);
function showNextSlide() {
if (index >= slides.length) {
index = 0;
}
document.getElementById('earth').src = slides[index++];
}
});
Try this (pure JS)
var myArray = ['img1', 'img2', 'img3', 'img4', 'img5', 'img6']
var count = 0;
setInterval(function() {
//use this below line if you want random images
//var rand = myArray[Math.floor(Math.random() * myArray.length)];
if (count >= myArray.length) count = 0; // if it is last image then show the first image.
// use this below line if you want images in order.
var rand = myArray[count];
document.getElementById('img').src = rand;
document.getElementById('img').alt = rand; // use 'alt' to display the image name if image is not found
count++;
}, 1000); // 1000 = 1 second
<img src="img/img 1.jpg" alt="image" id='img' />
To do this, you're going to need some Javascript to change the image. Here is a link to a popular website for help with Javascript, HTML, CSS, and a whole lot more. What you'll want to be looking at specifically though, is the setInterval() function on this page: http://www.w3schools.com/js/js_timing.asp
If you don't know Javascript at all, it is also not a bad place to start learning! If that's all you need it for though, you'll need very little Javascript at all.
Firstly include jQuery library in your page.
Then use this script:
$(document).ready(function() {
setInterval(function(){
_path = $('img').attr('src');
_img_id = _path.replace('img/img', '');
_img_id = _img_id.replace('.jpg', '');
_img_id++;
if (_img_id == 3) {
_img_id = 1;
};
$('img').attr('src', 'img/img' + _img_id + '.jpg');
}, 15000);
});

Update/Replace inline image on Google Document

I'm trying to set a feature to update images on a Google Document, the same way Lucidchart Add-on does on its "Updated inserted diagram" feature. For this, I'm current doing the following:
Creating a Named Range and storing its id on document properties, together with the data to generate the image, for later retrieve.
On update, call body.getNamedRangeById() and replace the element with the new generated image.
This works, but I have the following problems that does not happen with Lucidchart:
Every update, a blank line is added after the image.
If the user drag and drop the image inside document for reposition it, the Named Range disappears and I'm not able to retrieve it later.
If the user centralize the image, after update the image comes back to left position, even copying its attributes
Does anybody knows a good strategy to replace/update a referenced image on Google Docs, the same way Lucidchart add-on update feature works?
Thanks
NamedRanges indeed get lost when the range is moved, so they're not very good for your scenario. But there's no other way of identifying elements (which is a great misfeature of Google Docs).
In the case of an image you could use its LINK_URL to identify it, which seems to be what Lucidchart uses. It does not get in the way of the user, so it may be a good solution.
About getting a blank line and losing attributes when inserting an image, I imagine (since you haven't shared any code) you're inserting the image directly in the document body instead of a paragraph. Then a paragraph gets created automatically to wrap your image resulting in the blank line and lost of attributes.
Here's some code example:
function initialInsert() {
var data = Charts.newDataTable().addColumn(
Charts.ColumnType.STRING, 'Fruits').addColumn(
Charts.ColumnType.NUMBER, 'Amount').addRow(
['Apple',15]).addRow(
['Orange',6]).addRow(
['Banana',14]).build();
var chart = Charts.newPieChart().setDataTable(data).build();
var body = DocumentApp.getActiveDocument().getBody()
body.appendImage(chart).setLinkUrl('http://mychart');
//here we're inserting directly in the body, a wrapping paragraph element will be created for us
}
function updateImage() {
var data = Charts.newDataTable().addColumn(
Charts.ColumnType.STRING, 'Fruits').addColumn(
Charts.ColumnType.NUMBER, 'Amount').addRow(
['Apple',Math.floor(Math.random()*31)]).addRow( //random int between 0 and 30
['Orange',Math.floor(Math.random()*31)]).addRow(
['Banana',Math.floor(Math.random()*31)]).build();
var chart = Charts.newPieChart().setDataTable(data).build();
var img = getMyImg(DocumentApp.getActiveDocument().getBody(), 'http://mychart');
//let's insert on the current parent instead of the body
var parent = img.getParent(); //probably a paragraph, but does not really matter
parent.insertInlineImage(parent.getChildIndex(img)+1, chart).setLinkUrl('http://mychart');
img.removeFromParent();
}
function getMyImg(docBody, linkUrl) {
var imgs = docBody.getImages();
for( var i = 0; i < imgs.length; ++i )
if( imgs[i].getLinkUrl() === linkUrl )
return imgs[i];
return null;
}
About the link_url, you could of course do like Lucidchart does and link back to your site. So it's not just broken for the user.
Take a look at my add-on called PlantUML Gizmo.
Here's the code to the insert image function, which deals with replacing images if there's already one selected:
function insertImage(imageDataUrl, imageUrl) {
/*
* For debugging cursor info
*/
// var cursor = DocumentApp.getActiveDocument().getCursor();
// Logger.log(cursor.getElement().getParent().getType());
// throw "cursor info: " + cursor.getElement().getType() + " offset = " + cursor.getOffset() + " surrounding text = '" + cursor.getSurroundingText().getText() + "' parent's type = " +
// cursor.getElement().getParent().getType();
/*
* end debug
*/
var doc = DocumentApp.getActiveDocument();
var selection = doc.getSelection();
var replaced = false;
if (selection) {
var elements = selection.getSelectedElements();
// delete the selected image (to be replaced)
if (elements.length == 1 &&
elements[0].getElement().getType() ==
DocumentApp.ElementType.INLINE_IMAGE) {
var parentElement = elements[0].getElement().getParent(); // so we can re-insert cursor
elements[0].getElement().removeFromParent();
replaced = true;
// move cursor to just before deleted image
doc.setCursor(DocumentApp.getActiveDocument().newPosition(parentElement, 0));
} else {
throw "Please select only one image (image replacement) or nothing (image insertion)"
}
}
var cursor = doc.getCursor();
var blob;
if (imageDataUrl != "") {
blob = getBlobFromBase64(imageDataUrl);
} else {
blob = getBlobViaFetch(imageUrl);
}
var image = cursor.insertInlineImage(blob);
image.setLinkUrl(imageUrl);
// move the cursor to after the image
var position = doc.newPosition(cursor.getElement(), cursor.getOffset()+1);
doc.setCursor(position);
if (cursor.getElement().getType() == DocumentApp.ElementType.PARAGRAPH) {
Logger.log("Resizing");
// resize if wider than current page
var currentParagraph = DocumentApp.getActiveDocument().getCursor().getElement().asParagraph();
var originalImageWidth = image.getWidth(); // pixels
var documentWidthPoints = DocumentApp.getActiveDocument().getBody().getPageWidth() - DocumentApp.getActiveDocument().getBody().getMarginLeft() - DocumentApp.getActiveDocument().getBody().getMarginRight();
var documentWidth = documentWidthPoints * 96 / 72; // convert to pixels (a guess)
var paragraphWidthPoints = documentWidthPoints - currentParagraph.getIndentStart() - currentParagraph.getIndentEnd();
var paragraphWidth = paragraphWidthPoints * 96 / 72; // convert to pixels (a guess)
if (originalImageWidth > paragraphWidth) {
image.setWidth(paragraphWidth);
// scale proportionally
image.setHeight(image.getHeight() * image.getWidth() / originalImageWidth);
}
}
}

Is possible create map html area in percentage?

I need to create something like this:
http://www.mrporter.com/journal/journal_issue71/2#2
where every product in my big image is associated with a tooltip which appears on mouse hover.
But I need this to work with fullscreen images.
The first solution I thought (as the example above) is the map html solution where each fill up exactly the boundaries of my products.
The problem is that I can't indicate precise values for my because my image size depends on window screen.
The best solution would be the possibility to set percentage values for my area.
Is this possible? Any other suggestions ?
Alternative solution using links:
CSS:
.image{
position: relative;
}
.image a{
display: block;
position: absolute;
}
HTML:
<div class="image">
<img src="image.jpg" alt="image" />
</div>
Percentage dimensions can be detected in graphic editors
There is a jQuery plugin for this jQuery RWD Image Maps.
You might want to integrate my pending pull request (manually) to support "width=100%": https://github.com/stowball/jQuery-rwdImageMaps/pull/10
you can check this this plugin is life saving.
Useful when you want to map a percentage scaled image etc.
It can be used with or without jQuery.
https://github.com/davidjbradshaw/imagemap-resizer
and you can see it working at.
http://davidjbradshaw.com/imagemap-resizer/example/
Because this can't be done with simple HTML/CSS manipulation, the only alternative is JavaScript to, effectively, recalculate the coordinates based on the resizing of the image. To this end I've put together a function (though there's two functions involved) that achieves this end:
function findSizes(el, src) {
if (!el || !src) {
return false;
}
else {
var wGCS = window.getComputedStyle,
pI = parseInt,
dimensions = {};
dimensions.actualWidth = pI(wGCS(el, null).width.replace('px', ''), 10);
var newImg = document.createElement('img');
newImg.src = src;
newImg.style.position = 'absolute';
newImg.style.left = '-10000px';
document.body.appendChild(newImg);
dimensions.originalWidth = newImg.width;
document.body.removeChild(newImg);
return dimensions;
}
}
function remap(imgElem) {
if (!imgElem) {
return false;
}
else {
var mapName = imgElem
.getAttribute('usemap')
.substring(1),
map = document.getElementsByName(mapName)[0],
areas = map.getElementsByTagName('area'),
imgSrc = imgElem.src,
sizes = findSizes(imgElem, imgSrc),
currentWidth = sizes.actualWidth,
originalWidth = sizes.originalWidth,
multiplier = currentWidth / originalWidth,
newCoords;
for (var i = 0, len = areas.length; i < len; i++) {
newCoords = areas[i]
.getAttribute('coords')
.replace(/(\d+)/g,function(a){
return Math.round(a * multiplier);
});
areas[i].setAttribute('coords',newCoords);
}
}
}
var imgElement = document.getElementsByTagName('img')[0];
remap(imgElement);​
JS Fiddle demo.
Please note, though, that this requires a browser that implements window.getComputedStyle() (most current browsers, but only in IE from version 9, and above). Also, there are no sanity checks other than ensuring the required arguments are passed into the functions. These should, though, be a start if you want to experiment.
References:
document.body.
document.createElement().
document.getElementsByName().
document.getElementsByTagName().
element.getAttribute().
element.setAttribute().
element.style.
Math.round().
node.appendChild().
node.removeChild().
parseInt().
string.replace().
string.substring().
window.getComputedStyle.
Percentages in image maps are not an option. You might want to get some scripting involved (JS) that recalculates the exact position on images resize. Of course, in that script you can work with percentages if you want.
Consider using the Raphaël JavaScript Library with some CSS. See http://raphaeljs.com/ and Drawing over an image using Raphael.js.
I know this is an old question but maybe someone needs this at some point as I did. I modified #David Thomas' answer a bit to be have this little piece of JS be able to handle future recalculations:
function findSizes(el, src) {
if (!el || !src) {
return false;
}
else {
var wGCS = window.getComputedStyle,
pI = parseInt,
dimensions = {};
dimensions.actualWidth = pI(wGCS(el, null).width.replace('px', ''), 10);
var newImg = document.createElement('img');
newImg.src = src;
newImg.style.position = 'absolute';
newImg.style.left = '-10000px';
document.body.appendChild(newImg);
dimensions.originalWidth = newImg.width;
document.body.removeChild(newImg);
return dimensions;
}
}
function remap(imgElem) {
if (!imgElem) {
return false;
}
else {
var mapName = imgElem
.getAttribute('usemap')
.substring(1),
map = document.getElementsByName(mapName)[0],
areas = map.getElementsByTagName('area'),
imgSrc = imgElem.src,
sizes = findSizes(imgElem, imgSrc),
currentWidth = sizes.actualWidth,
originalWidth = sizes.originalWidth,
multiplier = currentWidth / originalWidth,
newCoords;
for (var i = 0, len = areas.length; i < len; i++) {
// Save original coordinates for future use
var originalCoords = areas[i].getAttribute('data-original-coords');
if (originalCoords == undefined) {
originalCoords = areas[i].getAttribute('coords');
areas[i].setAttribute('data-original-coords', originalCoords);
}
newCoords = originalCoords.replace(/(\d+)/g,function(a){
return Math.round(a * multiplier);
});
areas[i].setAttribute('coords',newCoords);
}
}
}
function remapImage() {
var imgElement = document.getElementsByTagName('img')[0];
remap(imgElement);
}
// Add a window resize event listener
var addEvent = function(object, type, callback) {
if (object == null || typeof(object) == 'undefined') return;
if (object.addEventListener) {
object.addEventListener(type, callback, false);
} else if (object.attachEvent) {
object.attachEvent("on" + type, callback);
} else {
object["on"+type] = callback;
}
};
addEvent(window, "resize", remapImage);

How Does This Background Image Work?

So This website:
http://www.atomicdust.com/
They have a background image on every page but when zooming, it doesn't change - ever. Not to mention how fast it loads. How is that possible to have content zoom without the background image? I could understand if it was a repeated image but this is not.
On that site it looks as though it's done by a script, as #rownage observes, but I've done it successfully in modern browsers using the (CSS3) "cover" property, i.e. background-size: cover; According to my notes (hurrah for comments!) this A List Apart article is where I got my information from.
On my (very much still-in-progress!) photo site, it looks like this at the moment. The background stays the same size when zooming in the browsers I've tried. Though it wouldn't surprise me if Internet Explorer couldn't cope with it.
As to how fast the images on that site load, the trick is simply a good choice of compressible image, well-compressed. The background that loaded when I visited their site is 1024x680 pixels, but because it's mostly black and white with large areas of plain colour, it compresses down to a pretty tiny 74KB.
It uses jQuery and this:
$.fn.bgResize = function(options) {
var defaults = {
imageWidth: 800,
imageHeight: 600
};
var options = $.extend(defaults, options);
var obj = $(this);
var initHtml = obj.html();
var windowHeight = $(window).height();
var windowWidth = $(window).width();
obj.height(windowHeight);
obj.width(windowWidth);
$('#work-wrapper').height(windowHeight);
$('#work-wrapper').width(windowWidth);
obj.html('<div id="inner-bg">'+initHtml+'</div>');
$('#inner-bg img').each(function(){
$(this).css({'display' : 'block', 'width' : '100%', 'height' : '100%'})
});
function doResize(){
var screenheight = $(window).height();
var screenwidth = $(window).width();
var imageheight = options.imageheight;
var imagewidth = options.imagewidth;
var ratio = imagewidth/imageheight;
var testwidth = screenheight * ratio;
var testheight = screenwidth / ratio;
obj.height(screenheight);
obj.width(screenwidth);
$('#work-wrapper').height(screenheight);
$('#work-wrapper').width(screenwidth);
if (testheight < screenheight){
obj.children('#inner-bg').width(testwidth);
obj.children('#inner-bg').height(testwidth/ratio);
var finalheight = Math.round(testwidth/ratio);
var finalwidth = testwidth;
var topoffset = (finalheight - screenheight)/2;
var leftoffset = (finalwidth - screenwidth)/2;
}
else if (testheight > screenheight){
obj.children('#inner-bg').height(testheight);
obj.children('#inner-bg').width(testheight * ratio);
var finalwidth = Math.round(testheight * ratio);
var finalheight = testheight;
var topoffset = (finalheight - screenheight)/2;
var leftoffset = (finalwidth - screenwidth)/2;
}
else {}
obj.children('#inner-bg').css("top", -topoffset);
obj.children('#inner-bg').css("left", -leftoffset);
}
doResize();
$(window).resize(function(){
doResize();
});
return this;
};