jQCloud
jQCloud copied to clipboard
autoresize lead to overlapping words
On using JQcloud in a bootstrap modal. If I use autoresize event in JQCloud, everything works fine if bootstrap modal is opened and zoom operation is performed but when the bootstrap modal is closed and zoom operations are performed on the browser then if the modal is opened again then entire words are being overlapped.
I tried it by removing autoresize: true then it worked fine. But some words are being overlapped now because of performing zoom-in operations many times.
So, is there any way to perform autoresize without using "autoresize: true" or is there any way to prevent overlapping of words on using autoresize
Maybe it's a resize timing problem. Internally, it is supposed to resize after 50 ms, but there are times when resize faster than the displayed timing. In this case, an override occurs because the dimension of the element is not set in the non-display state. First add below to jqcloud.js.
(function (factory) {
if (typeof define == 'function' && define.amd) {
// AMD
define(['jquery'], factory);
} else if (typeof exports === 'object') {
// Node, CommonJS
module.exports = factory(require('jquery'));
} else {
// Browser globals
factory(jQuery);
}
}(function ($) {
var inviewObjects = [], viewportSize, viewportOffset,
d = document, w = window, documentElement = d.documentElement, timer;
$.event.special.inview = {
add: function(data) {
inviewObjects.push({ data: data, $element: $(this), element: this });
// Use setInterval in order to also make sure this captures elements within
// "overflow:scroll" elements or elements that appeared in the dom tree due to
// dom manipulation and reflow
// old: $(window).scroll(checkInView);
//
// By the way, iOS (iPad, iPhone, ...) seems to not execute, or at least delays
// intervals while the user scrolls. Therefore the inview event might fire a bit late there
//
// Don't waste cycles with an interval until we get at least one element that
// has bound to the inview event.
if (!timer && inviewObjects.length) {
timer = setInterval(checkInView, 250);
}
},
remove: function(data) {
for (var i=0; i<inviewObjects.length; i++) {
var inviewObject = inviewObjects[i];
if (inviewObject.element === this && inviewObject.data.guid === data.guid) {
inviewObjects.splice(i, 1);
break;
}
}
// Clear interval when we no longer have any elements listening
if (!inviewObjects.length) {
clearInterval(timer);
timer = null;
}
}
};
function getViewportSize() {
var mode, domObject, size = { height: w.innerHeight, width: w.innerWidth };
// if this is correct then return it. iPad has compat Mode, so will
// go into check clientHeight/clientWidth (which has the wrong value).
if (!size.height) {
mode = d.compatMode;
if (mode || !$.support.boxModel) { // IE, Gecko
domObject = mode === 'CSS1Compat' ?
documentElement : // Standards
d.body; // Quirks
size = {
height: domObject.clientHeight,
width: domObject.clientWidth
};
}
}
return size;
}
function getViewportOffset() {
return {
top: w.pageYOffset || documentElement.scrollTop || d.body.scrollTop,
left: w.pageXOffset || documentElement.scrollLeft || d.body.scrollLeft
};
}
function checkInView() {
if (!inviewObjects.length) {
return;
}
var i = 0, $elements = $.map(inviewObjects, function(inviewObject) {
var selector = inviewObject.data.selector,
$element = inviewObject.$element;
return selector ? $element.find(selector) : $element;
});
viewportSize = viewportSize || getViewportSize();
viewportOffset = viewportOffset || getViewportOffset();
for (; i<inviewObjects.length; i++) {
// Ignore elements that are not in the DOM tree
if (!$.contains(documentElement, $elements[i][0])) {
continue;
}
var $element = $($elements[i]),
elementSize = { height: $element[0].offsetHeight, width: $element[0].offsetWidth },
elementOffset = $element.offset(),
inView = $element.data('inview');
// Don't ask me why because I haven't figured out yet:
// viewportOffset and viewportSize are sometimes suddenly null in Firefox 5.
// Even though it sounds weird:
// It seems that the execution of this function is interferred by the onresize/onscroll event
// where viewportOffset and viewportSize are unset
if (!viewportOffset || !viewportSize) {
return;
}
if (elementOffset.top + elementSize.height > viewportOffset.top &&
elementOffset.top < viewportOffset.top + viewportSize.height &&
elementOffset.left + elementSize.width > viewportOffset.left &&
elementOffset.left < viewportOffset.left + viewportSize.width) {
if (!inView) {
$element.data('inview', true).trigger('inview', [true]);
}
} else if (inView) {
$element.data('inview', false).trigger('inview', [false]);
}
}
}
$(w).on("scroll resize scrollstop", function() {
viewportSize = viewportOffset = null;
});
// IE < 9 scrolls to focused elements without firing the "scroll" event
if (!documentElement.addEventListener && documentElement.attachEvent) {
documentElement.attachEvent("onfocusin", function() {
viewportOffset = null;
});
}
}));
And change the initialize as below.
initialize: function() {
// Set/Get dimensions
if (this.options.width) {
this.$element.width(this.options.width);
}
else {
this.options.width = this.$element.width();
}
if (this.options.height) {
this.$element.height(this.options.height);
}
else {
this.options.height = this.$element.height();
}
// Default options value
this.options = $.extend(true, {}, jQCloud.DEFAULTS, this.options);
// Ensure delay
if (this.options.delay === null) {
this.options.delay = this.word_array.length > 50 ? 10 : 0;
}
// Backward compatibility
if (this.options.center.x > 1) {
this.options.center.x = this.options.center.x / this.options.width;
this.options.center.y = this.options.center.y / this.options.height;
}
// Create colorGenerator function from options
// Direct function
if (typeof this.options.colors == 'function') {
this.colorGenerator = this.options.colors;
}
// Array of sizes
else if ($.isArray(this.options.colors)) {
var cl = this.options.colors.length;
if (cl > 0) {
// Fill the sizes array to X items
if (cl < this.options.steps) {
for (var i = cl; i < this.options.steps; i++) {
this.options.colors[i] = this.options.colors[cl - 1];
}
}
this.colorGenerator = function(weight) {
return this.options.colors[this.options.steps - weight];
};
}
}
// Create sizeGenerator function from options
// Direct function
if (typeof this.options.fontSize == 'function') {
this.sizeGenerator = this.options.fontSize;
}
// Object with 'from' and 'to'
else if ($.isPlainObject(this.options.fontSize)) {
this.sizeGenerator = function(width, height, weight) {
var max = width * this.options.fontSize.from,
min = width * this.options.fontSize.to;
return Math.round(min + (max - min) * 1.0 / (this.options.steps - 1) * (weight - 1)) + 'px';
};
}
// Array of sizes
else if ($.isArray(this.options.fontSize)) {
var sl = this.options.fontSize.length;
if (sl > 0) {
// Fill the sizes array to X items
if (sl < this.options.steps) {
for (var j = sl; j < this.options.steps; j++) {
this.options.fontSize[j] = this.options.fontSize[sl - 1];
}
}
this.sizeGenerator = function(width, height, weight) {
return this.options.fontSize[this.options.steps - weight];
};
}
}
this.data.angle = Math.random() * 6.28;
this.data.step = (this.options.shape === 'rectangular') ? 18.0 : 2.0;
this.data.aspect_ratio = this.options.width / this.options.height;
this.clearTimeouts();
// Namespace word ids to avoid collisions between multiple clouds
this.data.namespace = (this.$element.attr('id') || Math.floor((Math.random() * 1000000)).toString(36)) + '_word_';
this.$element.addClass('jqcloud');
// Container's CSS position cannot be 'static'
if (this.$element.css('position') === 'static') {
this.$element.css('position', 'relative');
}
// Delay execution so that the browser can render the page before the computatively intensive word cloud drawing
this.createTimeout($.proxy(this.drawWordCloud, this), 10);
// Attach window resize event
if (this.options.autoResize) {
$(window).on('resize.' + this.data.namespace, throttle(this.resize, 50, this));
}
//console.log("root : "+this.options.width+", "+this.options.height+"("+this.$element.width()+", "+this.$element.height()+")");
var parent = this;
this.$element.on('inview',function(event,isInView){
if(isInView){
//console.log("show:"+$(this).width() + "," + $(this).height());
parent.resize();
}else{
//console.log("hide:"+$(this).width() + "," + $(this).height());
}
});
},
Finally, change resize as below.
resize: function() {
var new_size = {
width: this.$element.width(),
height: this.$element.height()
};
//console.log("resize:"+new_size.width + ", "+new_size.height);
//console.log("visible:"+this.$element.is(':visible'));
if (this.$element.is(':visible') && (new_size.width != this.options.width || new_size.height != this.options.height)) {
this.options.width = new_size.width;
this.options.height = new_size.height;
this.data.aspect_ratio = this.options.width / this.options.height;
this.update(this.word_array);
}
},