I am using firefox to open my page but when I open multiple tabs, the browser console error "Uncaught TypeError: this.containerDim is undefined". If I only open 1 tab then it works fine. This doesn't happen in chrome. Can someone answer for me?
Here is full code error
/*** picture view plugin ****/
(function ($, window, document, undefined) {
"use strict";
//an empty function
var noop = function () {};
var $body = $('body'),
$window = $(window),
$document = $(document);
//constants
var ZOOM_CONSTANT = 15; //increase or decrease value for zoom on mouse wheel
var MOUSE_WHEEL_COUNT = 5; //A mouse delta after which it should stop preventing default behaviour of mouse wheel
//ease out method
/*
t : current time,
b : intial value,
c : changed value,
d : duration
*/
function easeOutQuart(t, b, c, d) {
t /= d;
t--;
return -c * (t * t * t * t - 1) + b;
};
//function to check if image is loaded
function imageLoaded(img) {
return img.complete && (typeof img.naturalWidth === 'undefined' | img.naturalWidth !== 0);
}
var imageViewHtml = '<div class="iv-loader-ss"></div> <div class="iv-snap-view-ss">' + '<div class="iv-snap-image-wrap-ss">' + '<div class="iv-snap-handle-ss"></div>' + '</div>' + '<div class="iv-zoom-slider-ss" id="slider_zoom"><div class="iv-zoom-handle-ss"></div></div></div>' + '<div class="iv-image-view-ss" ><div class="iv-image-wrap-ss" ></div></div>';
//var imageViewHtml = '<div class="iv-zoom-slider id="slider_zoom"><div class="iv-zoom-handle"></div></div>';
var slider_zoom = '<div class="iv-zoom-slider">' + '<div class="iv-zoom-handle"></div>' + '</div>';
function Slider(container, options) {
this.container = container;
this.onStart = options.onStart || noop;
this.onMove = options.onMove || noop;
this.onEnd = options.onEnd || noop;
this.sliderId = options.sliderId || 'slider' + Math.ceil(Math.random() * 1000000);
}
Slider.prototype.init = function () {
var self = this,
container = this.container,
eventSuffix = '.' + this.sliderId;
//assign event on snap image wrap
this.container.on('touchstart' + eventSuffix + ' mousedown' + eventSuffix, function (estart) {
estart.preventDefault();
var touchMove = (estart.type == "touchstart" ? "touchmove" : "mousemove") + eventSuffix,
touchEnd = (estart.type == "touchstart" ? "touchend" : "mouseup") + eventSuffix,
eOrginal = estart.originalEvent,
sx = eOrginal.clientX || eOrginal.touches[0].clientX,
sy = eOrginal.clientY || eOrginal.touches[0].clientY;
var start = self.onStart(estart, {
x: sx,
y: sy
});
if (start === false) return;
var moveListener = function (emove) {
emove.preventDefault();
eOrginal = emove.originalEvent;
//get the cordinates
var mx = eOrginal.clientX,
my = eOrginal.clientY;
self.onMove(emove, {
dx: mx - sx,
dy: my - sy,
mx: mx,
my: my
});
};
var endListener = function () {
$document.off(touchMove, moveListener);
$document.off(touchEnd, endListener);
self.onEnd();
};
$document.on(touchMove, moveListener);
$document.on(touchEnd, endListener);
});
return this;
}
function ImageViewerss(container, options) {
var self = this;
if (container.is('#iv-container-ss')) {
self._fullPage = true;
}
self.container = container;
options = self.options = $.extend({}, ImageViewerss.defaults, options);
self.zoomValue = 100;
if (!container.find('.snap-view').length) {
//container.append(slider_zoom)
container.prepend(imageViewHtml);
}
container.addClass('iv-container-ss');
if (container.css('position') == 'static') container.css('position', 'relative');
self.snapView = container.find('.iv-snap-view-ss');
self.snapImageWrap = container.find('.iv-snap-image-wrap-ss');
self.imageWrap = container.find('.iv-image-wrap-ss');
self.snapHandle = container.find('.iv-snap-handle-ss');
self.zoomHandle = container.find('.iv-zoom-handle-ss');
self._viewerId = 'iv' + Math.floor(Math.random() * 1000000);
}
ImageViewerss.prototype = {
constructor: ImageViewerss,
_init: function () {
var viewerss = this,
options = viewerss.options,
zooming = false, // tell weather we are zooming trough touch
container = this.container;
var eventSuffix = '.' + viewerss._viewerId;
//cache dom refrence
var snapHandle = this.snapHandle;
var snapImgWrap = this.snapImageWrap;
var imageWrap = this.imageWrap;
var snapSlider = new Slider(snapImgWrap, {
sliderId: viewerss._viewerId,
onStart: function () {
if (!viewerss.loaded) return false;
var handleStyle = snapHandle[0].style;
this.curHandleTop = parseFloat(handleStyle.top);
this.curHandleLeft = parseFloat(handleStyle.left);
this.handleWidth = parseFloat(handleStyle.width);
this.handleHeight = parseFloat(handleStyle.height);
this.width = snapImgWrap.width();
this.height = snapImgWrap.height();
//stop momentum on image
clearInterval(imageSlider.slideMomentumCheck);
cancelAnimationFrame(imageSlider.sliderMomentumFrame);
},
onMove: function (e, position) {
var xPerc = this.curHandleLeft + position.dx * 100 / this.width,
yPerc = this.curHandleTop + position.dy * 100 / this.height;
xPerc = Math.max(0, xPerc);
xPerc = Math.min(100 - this.handleWidth, xPerc);
yPerc = Math.max(0, yPerc);
yPerc = Math.min(100 - this.handleHeight, yPerc);
var containerDim = viewerss.containerDim,
imgWidth = viewerss.imageDim.w * (viewerss.zoomValue / 100),
imgHeight = viewerss.imageDim.h * (viewerss.zoomValue / 100),
imgLeft = imgWidth < containerDim.w ? (containerDim.w - imgWidth) / 2 : -imgWidth * xPerc / 100,
imgTop = imgHeight < containerDim.h ? (containerDim.h - imgHeight) / 2 : -imgHeight * yPerc / 100;
snapHandle.css({
top: yPerc + '%',
left: xPerc + '%'
})
viewerss.currentImg.css({
left: imgLeft,
top: imgTop
})
viewerss.compareImg.css({
left: imgLeft,
top: imgTop
})
}
}).init();
/*Add slide interaction to image*/
var imageSlider = viewerss._imageSlider = new Slider(imageWrap, {
sliderId: viewerss._viewerId,
onStart: function (e, position) {
if (!viewerss.loaded) return false;
if (zooming) return;
var self = this;
snapSlider.onStart();
self.imgWidth = viewerss.imageDim.w * viewerss.zoomValue / 100;
self.imgHeight = viewerss.imageDim.h * viewerss.zoomValue / 100;
self.positions = [position, position];
self.startPosition = position;
},
onMove: function (e, position) {
if (zooming) return;
this.currentPos = position;
snapSlider.onMove(e, {
dx: -position.dx * snapSlider.width / this.imgWidth,
dy: -position.dy * snapSlider.height / this.imgHeight
});
},
}).init();
/*Add zoom interation in mouse wheel*/
var changedDelta = 0;
imageWrap.on("mousewheel" + eventSuffix + " DOMMouseScroll" + eventSuffix, function (e) {
if(!options.zoomOnMouseWheel) return;
if (!viewerss.loaded) return;
//clear all animation frame and interval
viewerss._clearFrames();
// cross-browser wheel delta
var delta = Math.max(-1, Math.min(1, (e.originalEvent.wheelDelta || -e.originalEvent.detail))),
zoomValue = viewerss.zoomValue * (100 + delta * ZOOM_CONSTANT) / 100;
if(!(zoomValue >= 100 && zoomValue <= options.maxZoom)){
changedDelta += Math.abs(delta);
}
else{
changedDelta = 0;
}
if(changedDelta > MOUSE_WHEEL_COUNT) return;
e.preventDefault();
var contOffset = container.offset(),
x = (e.pageX || e.originalEvent.pageX) - contOffset.left,
y = (e.pageY || e.originalEvent.pageY) - contOffset.top;
viewerss.zoom(zoomValue, {
x: x,
y: y
});
});
//zoom in zoom out using zoom handle
var slider = viewerss.snapView.find('.iv-zoom-slider-ss');
var zoomSlider = new Slider(slider, {
sliderId: viewerss._viewerId,
onStart: function (eStart) {
if (!viewerss.loaded) return false;
this.leftOffset = slider.offset().left;
this.handleWidth = viewerss.zoomHandle.width();
this.onMove(eStart);
},
onMove: function (e, position) {
var newLeft = (e.pageX || e.originalEvent.touches[0].pageX) - this.leftOffset - this.handleWidth / 2;
newLeft = Math.max(0, newLeft);
newLeft = Math.min(viewerss._zoomSliderLength, newLeft);
var zoomValue = 100 + (options.maxZoom - 100) * newLeft / viewerss._zoomSliderLength;
viewerss.zoom(zoomValue);
}
}).init();
//calculate elments size on window resize
if (options.refreshOnResize) $window.on('resize' + eventSuffix, function () {
//viewerss.refresh()
});
},
//method to zoom images
zoom: function (perc, point) {
perc = Math.max(100, perc);
if ((mouse_hover_depth & show_snap_view) | (!show_snap_view & hover_my & !hover_hang2)){
point = {
x: point_x_depth,
y: point_y_depth
};
console.log("check run if");
console.log("containerDim 00: "+this.containerDim.w);
}else{
console.log("check run else");
point = point || {
x: this.containerDim.w / 2,
y: this.containerDim.h / 2
};
console.log("containerDim 01: "+this.containerDim.w);
console.log("containerDim 02: "+this.containerDim.h);
}
point_x_ir = point.x;
point_y_ir = point.y;
var self = this,
maxZoom = this.options.maxZoom,
curPerc = this.zoomValue,
curImg = this.currentImg,
compareImg = this.compareImg,
containerDim = this.containerDim,
curLeft = parseFloat(curImg.css('left')),
curTop = parseFloat(curImg.css('top'));
console.log("containerDim 1: "+containerDim);
self._clearFrames();
var step = 0;
//calculate base top,left,bottom,right
var containerDim = self.containerDim,
imageDim = self.imageDim;
console.log("self.imageDim: "+self.imageDim);
console.log("self.containerDim 2: "+self.containerDim);
console.log("containerDim 2: "+containerDim);
var baseLeft = (containerDim.w - imageDim.w) / 2,
baseTop = (containerDim.h - imageDim.h) / 2,
baseRight = containerDim.w - baseLeft,
baseBottom = containerDim.h - baseTop;
function zoom() {
step++;
if (step < 20) {
self._zoomFrame = requestAnimationFrame(zoom);
}
var tickZoom = Math.min(maxZoom, easeOutQuart(step, curPerc, perc - curPerc, 20));
var ratio = tickZoom / curPerc,
imgWidth = self.imageDim.w * tickZoom / 100,
imgHeight = self.imageDim.h * tickZoom / 100,
newLeft = -((point.x - curLeft) * ratio - point.x),
newTop = -((point.y - curTop) * ratio - point.y);
/
//fix for left and top
newLeft = Math.min(newLeft, baseLeft);
newTop = Math.min(newTop, baseTop);
//fix for right and bottom
if((newLeft + imgWidth) < baseRight){
newLeft = (self.containerDim.w - imgWidth) ; //newLeft - (newLeft + imgWidth - baseRig
}
if((newTop + imgHeight) < baseBottom){
newTop = (self.containerDim.h - imgHeight) ; //newLeft - (newLeft + imgWidth - baseRig
}
curImg.css({
height: imgHeight + 'px',
width: imgWidth + 'px',
left: newLeft + 'px',
top: newTop + 'px'
});
compareImg.css({
height: 'auto',
width: imgWidth + 'px',
left: newLeft + 'px',
top: newTop + 'px'
});
self.zoomValue = tickZoom;
self._resizeHandle(imgWidth, imgHeight, newLeft, newTop);
//update zoom handle position
self.zoomHandle.css('left', ((tickZoom - 100) * self._zoomSliderLength) / (maxZoom - 100) + 'px');
}
zoom();
},
_clearFrames: function () {
//clearInterval(this._imageSlider.slideMomentumCheck);
cancelAnimationFrame(this._imageSlider.sliderMomentumFrame);
cancelAnimationFrame(this._zoomFrame)
},
//ko có vẫn chạy đc
resetZoom: function () {
this.zoom(this.options.zoomValue);
},
//calculate dimensions of image, container and reset the image
_calculateDimensions: function () {
//calculate content width of image and snap image
var self = this,
curImg = self.currentImg,
compareImg = self.compareImg,
container = self.container,
snapView = self.snapView,
imageWidth = curImg.width(),
imageHeight = curImg.height(),
contWidth = container.width(),
contHeight = container.height(),
snapViewWidth = snapView.innerWidth(),
snapViewHeight = snapView.innerHeight();
//set the container dimension
self.containerDim = {
w: contWidth,
h: contHeight
}
//set the image dimension
var imgWidth, imgHeight, ratio = imageWidth / imageHeight;
imgWidth = (imageWidth > imageHeight && contHeight >= contWidth) || ratio * contHeight > contWidth ? contWidth : ratio * contHeight;
imgHeight = imgWidth / ratio;
self.imageDim = {
w: imgWidth,
h: imgHeight
}
compareImg.css({
width: imgWidth + 'px',
height: 'auto',
left: (contWidth - imgWidth) / 2 + 'px',
top: (contHeight - imgHeight) / 2 + 'px',
'max-width': 'none',
'max-height': 'none'
});
curImg.css({
width: imgWidth + 'px',
height: imgHeight + 'px',
left: (contWidth - imgWidth) / 2 + 'px',
top: (contHeight - imgHeight) / 2 + 'px',
'max-width': 'none',
'max-height': 'none'
});
//set the snap Image dimension
var snapWidth = imgWidth > imgHeight ? snapViewWidth : imgWidth * snapViewHeight / imgHeight,
snapHeight = imgHeight > imgWidth ? snapViewHeight : imgHeight * snapViewWidth / imgWidth;
//ko có cũng ko sao
self.snapImageDim = {
w: snapWidth,
h: snapHeight
}
self.snapImg.css({
width: snapWidth,
height: snapHeight
});
//calculate zoom slider area
self._zoomSliderLength = snapViewWidth - self.zoomHandle.outerWidth();
},
refresh: function () {
if (!this.loaded) return;
this._calculateDimensions();
this.resetZoom();
},
_resizeHandle: function (imgWidth, imgHeight, imgLeft, imgTop) {
var curImg = this.currentImg,
imageWidth = imgWidth || this.imageDim.w * this.zoomValue / 100,
imageHeight = imgHeight || this.imageDim.h * this.zoomValue / 100,
left = Math.max(-(imgLeft || parseFloat(curImg.css('left'))) * 100 / imageWidth, 0),
top = Math.max(-(imgTop || parseFloat(curImg.css('top'))) * 100 / imageHeight, 0),
handleWidth = Math.min(this.containerDim.w * 100 / imageWidth, 100),
handleHeight = Math.min(this.containerDim.h * 100 / imageHeight, 100);
this.snapHandle.css({
top: top + '%',
left: left + '%',
width: handleWidth + '%',
height: handleHeight + '%'
});
},
load: function (image, hiResImg, compare, hiResCompare) {
var self = this,
container = this.container;
container.find('.iv-snap-image-ss,.iv-large-image-ss').remove();
var snapImageWrap = this.container.find('.iv-snap-image-wrap-ss');
snapImageWrap.prepend('<img class="iv-snap-image-ss" style="width:120px; height: 90px;" src="' + image + '" />');
this.imageWrap.prepend('<img class="iv-large-image-ss" src="' + image + '" />');
if (hiResImg) {
this.imageWrap.append('<img class="iv-large-image-ss" src="' + hiResImg + '" />')
}
if(compare) {
this.imageWrap.append('<img class="iv-large-compare-ss" src="' + compare + '" />');
}
var currentImg = this.currentImg = this.container.find('.iv-large-image-ss');
var compareImg = this.compareImg = this.container.find('.iv-large-compare');
this.snapImg = this.container.find('.iv-snap-image-ss');
self.loaded = false;
//show loader
container.find('.iv-loader-ss').show();
currentImg.hide();
compareImg.css('opacity', 0);
self.snapImg.hide();
//refresh the view
function refreshView() {
self.loaded = true;
self.zoomValue = 100;
//reset zoom of images
currentImg.show();
self.snapImg.show();
self.refresh();
self.resetZoom();
//hide loader
container.find('.iv-loader-ss').hide();
}
if (imageLoaded(currentImg[0])) {
refreshView();
} else {
$(currentImg[0]).on('load', refreshView);
}
},
setCompareAlpha: function(alpha) {
/*Add shift binding for image compare toggle*/
this.compareImg.css('opacity', alpha)
}
}
ImageViewerss.defaults = {
zoomValue: 100,
snapView: true,
maxZoom: 300,
refreshOnResize: true,
zoomOnMouseWheel : true
}
window.ImageViewerss = function (container, options) {
var imgElm, imgSrc, compareElm, compareSrc, hiResImg, hiResCompare;
if (!(container && (typeof container == "string" || container instanceof Element || container[0] instanceof Element))) {
options = container;
container = $('#iv-container-ss');
}
container = $(container);
if (container.is('img')) {
imgElm = container;
imgSrc = imgElm[0].src;
hiResImg = imgElm.attr('high-res-src') || imgElm.attr('data-high-res-src');
container = imgElm.wrap('<div class="iv-container-ss" style="display:inline-block; overflow:hidden"></div>').parent();
imgElm.css({
opacity: 0,
position: 'relative',
zIndex: -1
});
} else {
imgSrc = container.attr('src') || container.attr('data-src');
compareSrc = container.attr('compare-src') || container.attr('data-compare-src');
hiResImg = container.attr('high-res-src') || container.attr('data-high-res-src');
}
var viewerss = new ImageViewerss(container, options);
viewerss._init();
if (imgSrc) viewerss.load(imgSrc, hiResImg, compareSrc, hiResCompare);
return viewerss;
};
}((window.jQuery), window, document));
Here is the error code(The error line is the first line of the code below) :
//calculate base top,left,bottom,right
var containerDim = self.containerDim,
imageDim = self.imageDim;
console.log("self.imageDim: "+self.imageDim);
console.log("self.containerDim 2: "+self.containerDim);
console.log("containerDim 2: "+containerDim);
var baseLeft = (containerDim.w - imageDim.w) / 2,
baseTop = (containerDim.h - imageDim.h) / 2,
baseRight = containerDim.w - baseLeft,
baseBottom = containerDim.h - baseTop;
I'm call it like this:
if (mouseDown) {
ismouse = true
} else {
if (ismouse) {
$('.iv-large-image-ss').css('top', $('.iv-large-image').css('top'));
$('.iv-large-image-ss').css('left', $('.iv-large-image').css('left'));
ismouse = false
}
viewerss.zoom(rate);
}
"rate" is a number that I want to assign to represent the zoom level.
I tried to clear the firefox cache and ran the code above, but every time I open a new tab I have to clear the cache to run. I don't know why! :((
I am trying to get 3d transform effect of the bacground image on mouse move.
I have checked f.e. jquery.plate tilt.js and many others plugins, BUT, each of them has problem on chronium browsers like Chrome or Opera (it works fine even on IE11 -.-)
See the attachment and please adwise what is that and if it is fixable? The "dots" appear on mouse move (when background moves) randomly but in line within image.
(function($) {
'use strict';
var namespace = 'jquery-plate';
function Plate($element, options) {
this.config(options);
this.$container = $element;
if (this.options.element) {
if (typeof this.options.element === 'string') {
this.$element = this.$container.find(this.options.element);
} else {
this.$element = $(this.options.element);
}
} else {
this.$element = $element;
}
this.originalTransform = this.$element.css('transform');
this.$container
.on('mouseenter.' + namespace, this.onMouseEnter.bind(this))
.on('mouseleave.' + namespace, this.onMouseLeave.bind(this))
.on('mousemove.' + namespace, this.onMouseMove.bind(this));
}
Plate.prototype.config = function(options) {
this.options = $.extend({
inverse: false,
perspective: 500,
maxRotation: 10,
animationDuration: 200
}, this.options, options);
};
Plate.prototype.destroy = function() {
this.$element.css('transform', this.originalTransform);
this.$container.off('.' + namespace);
};
Plate.prototype.update = function(offsetX, offsetY, duration) {
var rotateX;
var rotateY;
if (offsetX || offsetX === 0) {
var height = this.$container.outerHeight();
var py = (offsetY - height / 2) / (height / 2);
rotateX = this.round(this.options.maxRotation * -py);
} else {
rotateY = 0;
}
if (offsetY || offsetY === 0) {
var width = this.$container.outerWidth();
var px = (offsetX - width / 2) / (width / 2);
rotateY = this.round(this.options.maxRotation * px);
} else {
rotateX = 0;
}
if (this.options.inverse) {
rotateX *= -1;
rotateY *= -1;
}
if (duration) {
this.animate(rotateX, rotateY, duration);
} else if (this.animation && this.animation.remaining) {
this.animation.targetX = rotateX;
this.animation.targetY = rotateY;
} else {
this.transform(rotateX, rotateY);
}
};
Plate.prototype.reset = function(duration) {
this.update(null, null, duration);
};
Plate.prototype.transform = function(rotateX, rotateY) {
this.currentX = rotateX;
this.currentY = rotateY;
this.$element.css('transform',
(this.originalTransform && this.originalTransform !== 'none' ? this.originalTransform + ' ' : '') +
'perspective(' + this.options.perspective + 'px) ' +
'rotateX(' + rotateX + 'deg) rotateY(' + rotateY + 'deg)'
);
};
Plate.prototype.animate = function(rotateX, rotateY, duration) {
if (duration) {
this.animation = this.animation || {};
var remaining = this.animation.remaining;
this.animation.time = performance.now();
this.animation.remaining = duration || null;
this.animation.targetX = rotateX;
this.animation.targetY = rotateY;
if (!remaining) {
requestAnimationFrame(this.onAnimationFrame.bind(this));
}
} else {
this.transform(rotateX, rotateY);
}
};
Plate.prototype.round = function(number) {
return Math.round(number * 1000) / 1000;
};
Plate.prototype.offsetCoords = function(event) {
var offset = this.$container.offset();
return {
x: event.pageX - offset.left,
y: event.pageY - offset.top
};
};
Plate.prototype.onAnimationFrame = function(timestamp) {
this.animation = this.animation || {};
var delta = timestamp - (this.animation.time || 0);
this.animation.time = timestamp;
var duration = this.animation.remaining || 0;
var percent = Math.min(delta / duration, 1);
var currentX = this.currentX || 0;
var currentY = this.currentY || 0;
var targetX = this.animation.targetX || 0;
var targetY = this.animation.targetY || 0;
var rotateX = this.round(currentX + (targetX - currentX) * percent);
var rotateY = this.round(currentY + (targetY - currentY) * percent);
this.transform(rotateX, rotateY);
var remaining = duration - delta;
this.animation.remaining = remaining > 0 ? remaining : null;
if (remaining > 0) {
requestAnimationFrame(this.onAnimationFrame.bind(this));
}
};
Plate.prototype.onMouseEnter = function(event) {
var offset = this.offsetCoords(event);
this.update(offset.x, offset.y, this.options.animationDuration);
};
Plate.prototype.onMouseLeave = function(event) {
this.reset(this.options.animationDuration);
};
Plate.prototype.onMouseMove = function(event) {
var offset = this.offsetCoords(event);
this.update(offset.x, offset.y);
};
$.fn.plate = function(options) {
return this.each(function() {
var $element = $(this);
var plate = $element.data(namespace);
if (options === 'remove') {
plate.destroy();
$element.data(namespace, null);
} else {
if (!plate) {
plate = new Plate($element, options);
$element.data(namespace, plate);
plate.reset();
} else {
plate.config(options);
}
}
});
};
})(jQuery);
$('#ab12cd').plate()
<div id="ab12cd" styles="width:100%;height:100%">
<img src="http://eskipaper.com/images/dark-background-8.jpg" />
</div>
<script
src="https://code.jquery.com/jquery-3.2.1.min.js"
integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
crossorigin="anonymous"></script>
// please open in new window, most visible effect you cen see if you move mouse bottom left/right
I cannot get cloudzoom to work with my woocommerce site, every time I choose an attribute ie size the main image disappears, it is like cloudzoom is trying to force an attribute image. I am absolutely pants at javascript and looking through the file I cannot see where I need to edit or delete the code. Any help would be greatly appreciated, I don't need the atribute image function to work at all so just stripping out the code would suit me. Here is the code...
(function ($) {
$(document).ready(function () {
$('.cloud-zoom, .cloud-zoom-gallery').CloudZoom();
});
function format(str) {
for (var i = 1; i < arguments.length; i++) {
str = str.replace('%' + (i - 1), arguments[i]);
}
return str;
}
function CloudZoom(jWin, opts) {
var sImg = $('img', jWin);
var img1;
var img2;
var zoomDiv = null;
var $mouseTrap = null;
var lens = null;
var $tint = null;
var softFocus = null;
var $ie6Fix = null;
var zoomImage;
var controlTimer = 0;
var cw, ch;
var destU = 0;
var destV = 0;
var currV = 0;
var currU = 0;
var filesLoaded = 0;
var mx,
my;
var ctx = this, zw;
// Display an image loading message. This message gets deleted when the images have loaded and the zoom init function is called.
// We add a small delay before the message is displayed to avoid the message flicking on then off again virtually immediately if the
// images load really fast, e.g. from the cache.
//var ctx = this;
setTimeout(function () {
// <img src="/images/loading.gif"/>
if ($mouseTrap === null) {
var w = jWin.width();
jWin.parent().append(format('<div style="width:%0px;position:absolute;top:75%;left:%1px;text-align:center" class="cloud-zoom-loading" >Loading...</div>', w / 3, (w / 2) - (w / 6))).find(':last').css('opacity', 0.5);
}
}, 200);
var ie6FixRemove = function () {
if ($ie6Fix !== null) {
$ie6Fix.remove();
$ie6Fix = null;
}
};
// Removes cursor, tint layer, blur layer etc.
this.removeBits = function () {
//$mouseTrap.unbind();
if (lens) {
lens.remove();
lens = null;
}
if ($tint) {
$tint.remove();
$tint = null;
}
if (softFocus) {
softFocus.remove();
softFocus = null;
}
ie6FixRemove();
$('.cloud-zoom-loading', jWin.parent()).remove();
};
this.destroy = function () {
jWin.data('zoom', null);
if ($mouseTrap) {
$mouseTrap.unbind();
$mouseTrap.remove();
$mouseTrap = null;
}
if (zoomDiv) {
zoomDiv.remove();
zoomDiv = null;
}
//ie6FixRemove();
this.removeBits();
// DON'T FORGET TO REMOVE JQUERY 'DATA' VALUES
};
// This is called when the zoom window has faded out so it can be removed.
this.fadedOut = function () {
if (zoomDiv) {
zoomDiv.remove();
zoomDiv = null;
}
this.removeBits();
//ie6FixRemove();
};
this.controlLoop = function () {
if (lens) {
var x = (mx - sImg.offset().left - (cw * 0.5)) >> 0;
var y = (my - sImg.offset().top - (ch * 0.5)) >> 0;
if (x < 0) {
x = 0;
}
else if (x > (sImg.outerWidth() - cw)) {
x = (sImg.outerWidth() - cw);
}
if (y < 0) {
y = 0;
}
else if (y > (sImg.outerHeight() - ch)) {
y = (sImg.outerHeight() - ch);
}
lens.css({
left: x,
top: y
});
lens.css('background-position', (-x) + 'px ' + (-y) + 'px');
destU = (((x) / sImg.outerWidth()) * zoomImage.width) >> 0;
destV = (((y) / sImg.outerHeight()) * zoomImage.height) >> 0;
currU += (destU - currU) / opts.smoothMove;
currV += (destV - currV) / opts.smoothMove;
zoomDiv.css('background-position', (-(currU >> 0) + 'px ') + (-(currV >> 0) + 'px'));
}
controlTimer = setTimeout(function () {
ctx.controlLoop();
}, 30);
};
this.init2 = function (img, id) {
filesLoaded++;
//console.log(img.src + ' ' + id + ' ' + img.width);
if (id === 1) {
zoomImage = img;
}
//this.images[id] = img;
if (filesLoaded === 2) {
this.init();
}
};
/* Init function start. */
this.init = function () {
// Remove loading message (if present);
$('.cloud-zoom-loading', jWin.parent()).remove();
/* Add a box (mouseTrap) over the small image to trap mouse events.
It has priority over zoom window to avoid issues with inner zoom.
We need the dummy background image as IE does not trap mouse events on
transparent parts of a div.
*/
$mouseTrap = jWin.parent().append(format("<div class='mousetrap' style='background-image:url(\".\");z-index:3;position:absolute;width:%0px;height:%1px;left:%2px;top:%3px;\'></div>", sImg.outerWidth(), sImg.outerHeight(), 0, 0)).find(':last');
//////////////////////////////////////////////////////////////////////
/* Do as little as possible in mousemove event to prevent slowdown. */
$mouseTrap.bind('mousemove', this, function (event) {
// Just update the mouse position
mx = event.pageX;
my = event.pageY;
});
//////////////////////////////////////////////////////////////////////
$mouseTrap.bind('mouseleave', this, function (event) {
clearTimeout(controlTimer);
//event.data.removeBits();
if(lens) { lens.fadeOut(299); }
if($tint) { $tint.fadeOut(299); }
if(softFocus) { softFocus.fadeOut(299); }
zoomDiv.fadeOut(300, function () {
ctx.fadedOut();
});
return false;
});
//////////////////////////////////////////////////////////////////////
$mouseTrap.bind('mouseenter', this, function (event) {
mx = event.pageX;
my = event.pageY;
zw = event.data;
if (zoomDiv) {
zoomDiv.stop(true, false);
zoomDiv.remove();
}
var xPos = opts.adjustX,
yPos = opts.adjustY;
var siw = sImg.outerWidth();
var sih = sImg.outerHeight();
var w = opts.zoomWidth;
var h = opts.zoomHeight;
if (opts.zoomWidth == 'auto') {
w = siw;
}
if (opts.zoomHeight == 'auto') {
h = sih;
}
//$('#info').text( xPos + ' ' + yPos + ' ' + siw + ' ' + sih );
var appendTo = jWin.parent(); // attach to the wrapper
switch (opts.position) {
case 'top':
yPos -= h; // + opts.adjustY;
break;
case 'right':
xPos += siw; // + opts.adjustX;
break;
case 'bottom':
yPos += sih; // + opts.adjustY;
break;
case 'left':
xPos -= w; // + opts.adjustX;
break;
case 'inside':
w = siw;
h = sih;
break;
// All other values, try and find an id in the dom to attach to.
default:
appendTo = $('#' + opts.position);
// If dom element doesn't exit, just use 'right' position as default.
if (!appendTo.length) {
appendTo = jWin;
xPos += siw; //+ opts.adjustX;
yPos += sih; // + opts.adjustY;
} else {
w = appendTo.innerWidth();
h = appendTo.innerHeight();
}
}
zoomDiv = appendTo.append(format('<div id="cloud-zoom-big" class="cloud-zoom-big" style="display:none;position:absolute;left:%0px;top:%1px;width:%2px;height:%3px;background-image:url(\'%4\');z-index:2;"></div>', xPos, yPos, w, h, zoomImage.src)).find(':last');
// Add the title from title tag.
if (sImg.attr('title') && opts.showTitle) {
zoomDiv.append(format('<div class="cloud-zoom-title">%0</div>', sImg.attr('title'))).find(':last').css('opacity', opts.titleOpacity);
}
// Fix ie6 select elements wrong z-index bug. Placing an iFrame over the select element solves the issue...
if ($.browser.msie && $.browser.version < 7) {
$ie6Fix = $('<iframe frameborder="0" src="#"></iframe>').css({
position: "absolute",
left: xPos,
top: yPos,
zIndex: 99,
width: w,
height: h
}).insertBefore(zoomDiv);
}
zoomDiv.fadeIn(500);
if (lens) {
lens.remove();
lens = null;
} /* Work out size of cursor */
cw = (sImg.outerWidth() / zoomImage.width) * zoomDiv.width();
ch = (sImg.outerHeight() / zoomImage.height) * zoomDiv.height();
// Attach mouse, initially invisible to prevent first frame glitch
lens = jWin.append(format("<div class = 'cloud-zoom-lens' style='display:none;z-index:1;position:absolute;width:%0px;height:%1px;opacity:0.4'></div>", cw, ch)).find(':last');
$mouseTrap.css('cursor', lens.css('cursor'));
var noTrans = false;
// Init tint layer if needed. (Not relevant if using inside mode)
if (opts.tint) {
/* lens.css('background', 'url("' + sImg.attr('src') + '")'); */
$tint = jWin.append(format('<div style="display:none;position:absolute; left:0px; top:0px; width:%0px; height:%1px; background-color:%2;" />', sImg.outerWidth(), sImg.outerHeight(), opts.tint)).find(':last');
$tint.css('opacity', opts.tintOpacity);
noTrans = true;
$tint.fadeIn(500);
}
if (opts.softFocus) {
lens.css('background', 'url("' + sImg.attr('src') + '")');
softFocus = jWin.append(format('<div style="position:absolute;display:none;top:2px; left:2px; width:%0px; height:%1px;" />', sImg.outerWidth() - 2, sImg.outerHeight() - 2, opts.tint)).find(':last');
softFocus.css('background', 'url("' + sImg.attr('src') + '")');
softFocus.css('opacity', 0.5);
noTrans = true;
softFocus.fadeIn(500);
}
if (!noTrans) {
lens.css('opacity', opts.lensOpacity);
}
if ( opts.position !== 'inside' ) { lens.fadeIn(500); }
// Start processing.
zw.controlLoop();
return; // Don't return false here otherwise opera will not detect change of the mouse pointer type.
});
};
img1 = new Image();
$(img1).load(function () {
ctx.init2(this, 0);
});
img1.src = sImg.attr('src');
img2 = new Image();
$(img2).load(function () {
ctx.init2(this, 1);
});
img2.src = jWin.attr('href');
}
$.fn.CloudZoom = function (options) {
// IE6 background image flicker fix
try {
document.execCommand("BackgroundImageCache", false, true);
} catch (e) {}
this.each(function () {
var relOpts, opts;
// Hmm...eval...slap on wrist.
eval('var a = {' + $(this).attr('rel') + '}');
relOpts = a;
if ($(this).is('.cloud-zoom')) {
$(this).css({
'position': 'relative',
'display': 'block'
});
$('img', $(this)).css({
'display': 'block'
});
// Wrap an outer div around the link so we can attach things without them becoming part of the link.
// But not if wrap already exists.
if ($(this).parent().attr('id') != 'wrap') {
$(this).wrap('<div id="wrap" style="top:0px;z-index:4;position:relative;"></div>');
}
opts = $.extend({}, $.fn.CloudZoom.defaults, options);
opts = $.extend({}, opts, relOpts);
$(this).data('zoom', new CloudZoom($(this), opts));
} else if ($(this).is('.cloud-zoom-gallery')) {
opts = $.extend({}, relOpts, options);
$(this).data('relOpts', opts);
$(this).bind('click', $(this), function (event) {
var data = event.data.data('relOpts');
// Destroy the previous zoom
$('#' + data.useZoom).data('zoom').destroy();
// Change the biglink to point to the new big image.
$('#' + data.useZoom).attr('href', event.data.attr('href'));
// Change the small image to point to the new small image.
$('#' + data.useZoom + ' img').attr('src', event.data.data('relOpts').smallImage);
// Init a new zoom with the new images.
$('#zoom-cb').attr('href', event.data.attr('href'));
$('#' + event.data.data('relOpts').useZoom).CloudZoom();
return false;
});
}
});
return this;
};
$.fn.CloudZoom.defaults = {
zoomWidth: 'auto',
zoomHeight: 'auto',
position: 'right',
tint: false,
tintOpacity: 0.5,
lensOpacity: 0.5,
softFocus: false,
smoothMove: 3,
showTitle: false,
titleOpacity: 0.5,
adjustX: 0,
adjustY: 0
};
})(jQuery);
see the news scroller on the top of this site
http://track.dc.gov/Agency/DH0
Any idea what library/functions this site uses to implment such a smooth scroller?
They have a very nicely formatted block of code you can study. Open your favorite JS debugger when you visit the site, wait for everything to get moving, and then press "Break All" or the equivalent in your debugger. You'll see something like the following:
Dashboard.UI.EndlessLine = function() {
var me = this;
me.jq = $(me);
me.classNames = { CONTAINER: "uiEndless", VIEW: "uiEndlessView", CANVAS: "uiEndlessCanvas", TILE: "uiEndlessTile" };
var canvas = null;
var view = null;
var tiles = null;
var x = 0;
var xx = 0;
var canvasWidth = 0;
var step = 1;
var delay = 40;
me.initialize = function(container, data, handler) {
required(container, "container");
required(data, "data");
required(handler, "handler");
container.addClass(me.classNames.CONTAINER);
view = newDiv(me.classNames.VIEW);
canvas = newDiv(me.classNames.CANVAS);
view.append(canvas);
container.append(view);
x = 0;
xx = 0;
canvasWidth = 0;
tiles = me.populateTiles(data, handler);
container.click(function() {
if (me.started()) me.stop(); else me.start();
});
};
me._resize = function(size) {
};
var moveId = 0;
me.start = function() {
me.stop();
me.tick();
}
me.stop = function() {
if (moveId > 0) clearTimeout(moveId);
moveId = 0;
}
me.started = function() {
return moveId > 0;
};
me.tick = function() {
var tile = tiles.current();
var width = tile.calculatedWidth;
if (x < width - step) {
x += step;
} else {
x = 0;
tile.css("left", canvasWidth + "px");
if (tiles.advance()) {
xx = 0;
canvasWidth = 0;
do {
current = tiles.current();
width = current.calculatedWidth;
current[0].style.left = canvasWidth + "px";
canvasWidth += width;
} while (!tiles.advance());
} else {
canvasWidth += width;
}
}
canvas[0].style.left = -(xx) + "px";
xx += step;
moveId = setTimeout(me.tick, delay);
}
me.populateTiles = function(data, handler) {
var tiles = new Dashboard.Core.List();
var viewWidth = view.contentWidth();
var maxHeight = 0;
each(data, function() {
var tile = newDiv(me.classNames.TILE);
handler.call(this, tile);
tile.css({ left: canvasWidth + "px", top: 0 });
canvas.append(tile);
var width = tile.outerWidth();
var height = tile.outerHeight();
if (maxHeight < height) maxHeight = height;
tile.calculatedWidth = width;
canvasWidth += width; // getting width may only be done after the element is attached to DOM
tiles.append(tile);
view.height(height);
});
return tiles.createCycle();
}
}
I'm impressed -- everything looks professional and nicely namespaced.
Update: If you want an explanation of how it works, focus on the tick method defined above. Glossing over all the details (cause I haven't really studied it myself), it calculates a step size, moves the message element to the left by the some amount, and schedules the next tick call for 40 milliseconds in the future.
jQuery enthusiast, Remy Sharp, has his own Marquee Plugin that you can implement pretty easily. You can gather deeper details of it on his blog or by visiting the demo page.
For Mootools users, there's Mooquee.
You can also view the actual code for this example online at http://track.dc.gov/Resource/Script/ - do a search for "uiEndless" to find the target-scripting.