123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720 |
- // wrapped by build app
- define("dojox/mobile/app/ImageView", ["dijit","dojo","dojox","dojo/require!dojox/mobile/app/_Widget,dojo/fx/easing"], function(dijit,dojo,dojox){
- dojo.provide("dojox.mobile.app.ImageView");
- dojo.experimental("dojox.mobile.app.ImageView");
- dojo.require("dojox.mobile.app._Widget");
- dojo.require("dojo.fx.easing");
- dojo.declare("dojox.mobile.app.ImageView", dojox.mobile.app._Widget, {
- // zoom: Number
- // The current level of zoom. This should not be set manually.
- zoom: 1,
- // zoomCenterX: Number
- // The X coordinate in the image where the zoom is focused
- zoomCenterX: 0,
- // zoomCenterY: Number
- // The Y coordinate in the image where the zoom is focused
- zoomCenterY: 0,
- // maxZoom: Number
- // The highest degree to which an image can be zoomed. For example,
- // a maxZoom of 5 means that the image will be 5 times larger than normal
- maxZoom: 5,
- // autoZoomLevel: Number
- // The degree to which the image is zoomed when auto zoom is invoked.
- // The higher the number, the more the image is zoomed in.
- autoZoomLevel: 3,
- // disableAutoZoom: Boolean
- // Disables auto zoom
- disableAutoZoom: false,
- // disableSwipe: Boolean
- // Disables the users ability to swipe from one image to the next.
- disableSwipe: false,
- // autoZoomEvent: String
- // Overrides the default event listened to which invokes auto zoom
- autoZoomEvent: null,
- // _leftImg: Node
- // The full sized image to the left
- _leftImg: null,
- // _centerImg: Node
- // The full sized image in the center
- _centerImg: null,
- // _rightImg: Node
- // The full sized image to the right
- _rightImg: null,
- // _leftImg: Node
- // The small sized image to the left
- _leftSmallImg: null,
- // _centerImg: Node
- // The small sized image in the center
- _centerSmallImg: null,
- // _rightImg: Node
- // The small sized image to the right
- _rightSmallImg: null,
- constructor: function(){
- this.panX = 0;
- this.panY = 0;
- this.handleLoad = dojo.hitch(this, this.handleLoad);
- this._updateAnimatedZoom = dojo.hitch(this, this._updateAnimatedZoom);
- this._updateAnimatedPan = dojo.hitch(this, this._updateAnimatedPan);
- this._onAnimPanEnd = dojo.hitch(this, this._onAnimPanEnd);
- },
- buildRendering: function(){
- this.inherited(arguments);
- this.canvas = dojo.create("canvas", {}, this.domNode);
- dojo.addClass(this.domNode, "mblImageView");
- },
- postCreate: function(){
- this.inherited(arguments);
- this.size = dojo.marginBox(this.domNode);
- dojo.style(this.canvas, {
- width: this.size.w + "px",
- height: this.size.h + "px"
- });
- this.canvas.height = this.size.h;
- this.canvas.width = this.size.w;
- var _this = this;
- // Listen to the mousedown/touchstart event. Record the position
- // so we can use it to pan the image.
- this.connect(this.domNode, "onmousedown", function(event){
- if(_this.isAnimating()){
- return;
- }
- if(_this.panX){
- _this.handleDragEnd();
- }
- _this.downX = event.targetTouches ? event.targetTouches[0].clientX : event.clientX;
- _this.downY = event.targetTouches ? event.targetTouches[0].clientY : event.clientY;
- });
- // record the movement of the mouse.
- this.connect(this.domNode, "onmousemove", function(event){
- if(_this.isAnimating()){
- return;
- }
- if((!_this.downX && _this.downX !== 0) || (!_this.downY && _this.downY !== 0)){
- // If the touch didn't begin on this widget, ignore the movement
- return;
- }
- if((!_this.disableSwipe && _this.zoom == 1)
- || (!_this.disableAutoZoom && _this.zoom != 1)){
- var x = event.targetTouches ?
- event.targetTouches[0].clientX : event.pageX;
- var y = event.targetTouches ?
- event.targetTouches[0].clientY : event.pageY;
- _this.panX = x - _this.downX;
- _this.panY = y - _this.downY;
- if(_this.zoom == 1){
- // If not zoomed in, then try to move to the next or prev image
- // but only if the mouse has moved more than 10 pixels
- // in the X direction
- if(Math.abs(_this.panX) > 10){
- _this.render();
- }
- }else{
- // If zoomed in, pan the image if the mouse has moved more
- // than 10 pixels in either direction.
- if(Math.abs(_this.panX) > 10 || Math.abs(_this.panY) > 10){
- _this.render();
- }
- }
- }
- });
- this.connect(this.domNode, "onmouseout", function(event){
- if(!_this.isAnimating() && _this.panX){
- _this.handleDragEnd();
- }
- });
- this.connect(this.domNode, "onmouseover", function(event){
- _this.downX = _this.downY = null;
- });
- // Set up AutoZoom, which zooms in a fixed amount when the user taps
- // a part of the canvas
- this.connect(this.domNode, "onclick", function(event){
- if(_this.isAnimating()){
- return;
- }
- if(_this.downX == null || _this.downY == null){
- return;
- }
- var x = (event.targetTouches ?
- event.targetTouches[0].clientX : event.pageX);
- var y = (event.targetTouches ?
- event.targetTouches[0].clientY : event.pageY);
- // If the mouse/finger has moved more than 14 pixels from where it
- // started, do not treat it as a click. It is a drag.
- if(Math.abs(_this.panX) > 14 || Math.abs(_this.panY) > 14){
- _this.downX = _this.downY = null;
- _this.handleDragEnd();
- return;
- }
- _this.downX = _this.downY = null;
- if(!_this.disableAutoZoom){
- if(!_this._centerImg || !_this._centerImg._loaded){
- // Do nothing until the image is loaded
- return;
- }
- if(_this.zoom != 1){
- _this.set("animatedZoom", 1);
- return;
- }
- var pos = dojo._abs(_this.domNode);
- // Translate the clicked point to a point on the source image
- var xRatio = _this.size.w / _this._centerImg.width;
- var yRatio = _this.size.h / _this._centerImg.height;
- // Do an animated zoom to the point which was clicked.
- _this.zoomTo(
- ((x - pos.x) / xRatio) - _this.panX,
- ((y - pos.y) / yRatio) - _this.panY,
- _this.autoZoomLevel);
- }
- });
- // Listen for Flick events
- dojo.connect(this.domNode, "flick", this, "handleFlick");
- },
- isAnimating: function(){
- // summary:
- // Returns true if an animation is in progress, false otherwise.
- return this._anim && this._anim.status() == "playing";
- },
- handleDragEnd: function(){
- // summary:
- // Handles the end of a dragging event. If not zoomed in, it
- // determines if the next or previous image should be transitioned
- // to.
- this.downX = this.downY = null;
- console.log("handleDragEnd");
- if(this.zoom == 1){
- if(!this.panX){
- return;
- }
- var leftLoaded = (this._leftImg && this._leftImg._loaded)
- || (this._leftSmallImg && this._leftSmallImg._loaded);
- var rightLoaded = (this._rightImg && this._rightImg._loaded)
- || (this._rightSmallImg && this._rightSmallImg._loaded);
- // Check if the drag has moved the image more than half its length.
- // If so, move to either the previous or next image.
- var doMove =
- !(Math.abs(this.panX) < this._centerImg._baseWidth / 2) &&
- (
- (this.panX > 0 && leftLoaded ? 1 : 0) ||
- (this.panX < 0 && rightLoaded ? 1 : 0)
- );
- if(!doMove){
- // If not moving to another image, animate the sliding of the
- // image back into place.
- this._animPanTo(0, dojo.fx.easing.expoOut, 700);
- }else{
- // Move to another image.
- this.moveTo(this.panX);
- }
- }else{
- if(!this.panX && !this.panY){
- return;
- }
- // Recenter the zoomed image based on where it was panned to
- // previously
- this.zoomCenterX -= (this.panX / this.zoom);
- this.zoomCenterY -= (this.panY / this.zoom);
- this.panX = this.panY = 0;
- }
- },
- handleFlick: function(event){
- // summary:
- // Handle a flick event.
- if(this.zoom == 1 && event.duration < 500){
- // Only handle quick flicks here, less than 0.5 seconds
- // If not zoomed in, then check if we should move to the next photo
- // or not
- if(event.direction == "ltr"){
- this.moveTo(1);
- }else if(event.direction == "rtl"){
- this.moveTo(-1);
- }
- // If an up or down flick occurs, it means nothing so ignore it
- this.downX = this.downY = null;
- }
- },
- moveTo: function(direction){
- direction = direction > 0 ? 1 : -1;
- var toImg;
- if(direction < 1){
- if(this._rightImg && this._rightImg._loaded){
- toImg = this._rightImg;
- }else if(this._rightSmallImg && this._rightSmallImg._loaded){
- toImg = this._rightSmallImg;
- }
- }else{
- if(this._leftImg && this._leftImg._loaded){
- toImg = this._leftImg;
- }else if(this._leftSmallImg && this._leftSmallImg._loaded){
- toImg = this._leftSmallImg;
- }
- }
- this._moveDir = direction;
- var _this = this;
- if(toImg && toImg._loaded){
- // If the image is loaded, make a linear animation to show it
- this._animPanTo(this.size.w * direction, null, 500, function(){
- _this.panX = 0;
- _this.panY = 0;
- if(direction < 0){
- // Moving to show the right image
- _this._switchImage("left", "right");
- }else{
- // Moving to show the left image
- _this._switchImage("right", "left");
- }
- _this.render();
- _this.onChange(direction * -1);
- });
- }else{
- // If the next image is not loaded, make an animation to
- // move the center image to half the width of the widget and back
- // again
- console.log("moveTo image not loaded!", toImg);
- this._animPanTo(0, dojo.fx.easing.expoOut, 700);
- }
- },
- _switchImage: function(toImg, fromImg){
- var toSmallImgName = "_" + toImg + "SmallImg";
- var toImgName = "_" + toImg + "Img";
- var fromSmallImgName = "_" + fromImg + "SmallImg";
- var fromImgName = "_" + fromImg + "Img";
- this[toImgName] = this._centerImg;
- this[toSmallImgName] = this._centerSmallImg;
- this[toImgName]._type = toImg;
- if(this[toSmallImgName]){
- this[toSmallImgName]._type = toImg;
- }
- this._centerImg = this[fromImgName];
- this._centerSmallImg = this[fromSmallImgName];
- this._centerImg._type = "center";
- if(this._centerSmallImg){
- this._centerSmallImg._type = "center";
- }
- this[fromImgName] = this[fromSmallImgName] = null;
- },
- _animPanTo: function(to, easing, duration, callback){
- this._animCallback = callback;
- this._anim = new dojo.Animation({
- curve: [this.panX, to],
- onAnimate: this._updateAnimatedPan,
- duration: duration || 500,
- easing: easing,
- onEnd: this._onAnimPanEnd
- });
- this._anim.play();
- return this._anim;
- },
- onChange: function(direction){
- // summary:
- // Stub function that can be listened to in order to provide
- // new images when the displayed image changes
- },
- _updateAnimatedPan: function(amount){
- this.panX = amount;
- this.render();
- },
- _onAnimPanEnd: function(){
- this.panX = this.panY = 0;
- if(this._animCallback){
- this._animCallback();
- }
- },
- zoomTo: function(centerX, centerY, zoom){
- this.set("zoomCenterX", centerX);
- this.set("zoomCenterY", centerY);
- this.set("animatedZoom", zoom);
- },
- render: function(){
- var cxt = this.canvas.getContext('2d');
- cxt.clearRect(0, 0, this.canvas.width, this.canvas.height);
- // Render the center image
- this._renderImg(
- this._centerSmallImg,
- this._centerImg,
- this.zoom == 1 ? (this.panX < 0 ? 1 : this.panX > 0 ? -1 : 0) : 0);
- if(this.zoom == 1 && this.panX != 0){
- if(this.panX > 0){
- // Render the left image, showing the right side of it
- this._renderImg(this._leftSmallImg, this._leftImg, 1);
- }else{
- // Render the right image, showing the left side of it
- this._renderImg(this._rightSmallImg, this._rightImg, -1);
- }
- }
- },
- _renderImg: function(smallImg, largeImg, panDir){
- // summary:
- // Renders a single image
- // If zoomed, we just display the center img
- var img = (largeImg && largeImg._loaded) ? largeImg : smallImg;
- if(!img || !img._loaded){
- // If neither the large or small image is loaded, display nothing
- return;
- }
- var cxt = this.canvas.getContext('2d');
- var baseWidth = img._baseWidth;
- var baseHeight = img._baseHeight;
- // Calculate the size the image would be if there were no bounds
- var desiredWidth = baseWidth * this.zoom;
- var desiredHeight = baseHeight * this.zoom;
- // Calculate the actual size of the viewable image
- var destWidth = Math.min(this.size.w, desiredWidth);
- var destHeight = Math.min(this.size.h, desiredHeight);
- // Calculate the size of the window on the original image to use
- var sourceWidth = this.dispWidth = img.width * (destWidth / desiredWidth);
- var sourceHeight = this.dispHeight = img.height * (destHeight / desiredHeight);
- var zoomCenterX = this.zoomCenterX - (this.panX / this.zoom);
- var zoomCenterY = this.zoomCenterY - (this.panY / this.zoom);
- // Calculate where the center of the view should be
- var centerX = Math.floor(Math.max(sourceWidth / 2,
- Math.min(img.width - sourceWidth / 2, zoomCenterX)));
- var centerY = Math.floor(Math.max(sourceHeight / 2,
- Math.min(img.height - sourceHeight / 2, zoomCenterY)));
- var sourceX = Math.max(0,
- Math.round((img.width - sourceWidth)/2 + (centerX - img._centerX)) );
- var sourceY = Math.max(0,
- Math.round((img.height - sourceHeight) / 2 + (centerY - img._centerY))
- );
- var destX = Math.round(Math.max(0, this.canvas.width - destWidth)/2);
- var destY = Math.round(Math.max(0, this.canvas.height - destHeight)/2);
- var oldDestWidth = destWidth;
- var oldSourceWidth = sourceWidth;
- if(this.zoom == 1 && panDir && this.panX){
- if(this.panX < 0){
- if(panDir > 0){
- // If the touch is moving left, and the right side of the
- // image should be shown, then reduce the destination width
- // by the absolute value of panX
- destWidth -= Math.abs(this.panX);
- destX = 0;
- }else if(panDir < 0){
- // If the touch is moving left, and the left side of the
- // image should be shown, then set the displayed width
- // to the absolute value of panX, less some pixels for
- // a padding between images
- destWidth = Math.max(1, Math.abs(this.panX) - 5);
- destX = this.size.w - destWidth;
- }
- }else{
- if(panDir > 0){
- // If the touch is moving right, and the right side of the
- // image should be shown, then set the destination width
- // to the absolute value of the pan, less some pixels for
- // padding
- destWidth = Math.max(1, Math.abs(this.panX) - 5);
- destX = 0;
- }else if(panDir < 0){
- // If the touch is moving right, and the left side of the
- // image should be shown, then reduce the destination width
- // by the widget width minus the absolute value of panX
- destWidth -= Math.abs(this.panX);
- destX = this.size.w - destWidth;
- }
- }
- sourceWidth = Math.max(1,
- Math.floor(sourceWidth * (destWidth / oldDestWidth)));
- if(panDir > 0){
- // If the right side of the image should be displayed, move
- // the sourceX to be the width of the image minus the difference
- // between the original sourceWidth and the new sourceWidth
- sourceX = (sourceX + oldSourceWidth) - (sourceWidth);
- }
- sourceX = Math.floor(sourceX);
- }
- try{
- // See https://developer.mozilla.org/en/Canvas_tutorial/Using_images
- cxt.drawImage(
- img,
- Math.max(0, sourceX),
- sourceY,
- Math.min(oldSourceWidth, sourceWidth),
- sourceHeight,
- destX, // Xpos
- destY, // Ypos
- Math.min(oldDestWidth, destWidth),
- destHeight
- );
- }catch(e){
- console.log("Caught Error",e,
- "type=", img._type,
- "oldDestWidth = ", oldDestWidth,
- "destWidth", destWidth,
- "destX", destX
- , "oldSourceWidth=",oldSourceWidth,
- "sourceWidth=", sourceWidth,
- "sourceX = " + sourceX
- );
- }
- },
- _setZoomAttr: function(amount){
- this.zoom = Math.min(this.maxZoom, Math.max(1, amount));
- if(this.zoom == 1
- && this._centerImg
- && this._centerImg._loaded){
- if(!this.isAnimating()){
- this.zoomCenterX = this._centerImg.width / 2;
- this.zoomCenterY = this._centerImg.height / 2;
- }
- this.panX = this.panY = 0;
- }
- this.render();
- },
- _setZoomCenterXAttr: function(value){
- if(value != this.zoomCenterX){
- if(this._centerImg && this._centerImg._loaded){
- value = Math.min(this._centerImg.width, value);
- }
- this.zoomCenterX = Math.max(0, Math.round(value));
- }
- },
- _setZoomCenterYAttr: function(value){
- if(value != this.zoomCenterY){
- if(this._centerImg && this._centerImg._loaded){
- value = Math.min(this._centerImg.height, value);
- }
- this.zoomCenterY = Math.max(0, Math.round(value));
- }
- },
- _setZoomCenterAttr: function(value){
- if(value.x != this.zoomCenterX || value.y != this.zoomCenterY){
- this.set("zoomCenterX", value.x);
- this.set("zoomCenterY", value.y);
- this.render();
- }
- },
- _setAnimatedZoomAttr: function(amount){
- if(this._anim && this._anim.status() == "playing"){
- return;
- }
- this._anim = new dojo.Animation({
- curve: [this.zoom, amount],
- onAnimate: this._updateAnimatedZoom,
- onEnd: this._onAnimEnd
- });
- this._anim.play();
- },
- _updateAnimatedZoom: function(amount){
- this._setZoomAttr(amount);
- },
- _setCenterUrlAttr: function(urlOrObj){
- this._setImage("center", urlOrObj);
- },
- _setLeftUrlAttr: function(urlOrObj){
- this._setImage("left", urlOrObj);
- },
- _setRightUrlAttr: function(urlOrObj){
- this._setImage("right", urlOrObj);
- },
- _setImage: function(name, urlOrObj){
- var smallUrl = null;
- var largeUrl = null;
- if(dojo.isString(urlOrObj)){
- // If the argument is a string, then just load the large url
- largeUrl = urlOrObj;
- }else{
- largeUrl = urlOrObj.large;
- smallUrl = urlOrObj.small;
- }
- if(this["_" + name + "Img"] && this["_" + name + "Img"]._src == largeUrl){
- // Identical URL, ignore it
- return;
- }
- // Just do the large image for now
- var largeImg = this["_" + name + "Img"] = new Image();
- largeImg._type = name;
- largeImg._loaded = false;
- largeImg._src = largeUrl;
- largeImg._conn = dojo.connect(largeImg, "onload", this.handleLoad);
- if(smallUrl){
- // If a url to a small version of the image has been provided,
- // load that image first.
- var smallImg = this["_" + name + "SmallImg"] = new Image();
- smallImg._type = name;
- smallImg._loaded = false;
- smallImg._conn = dojo.connect(smallImg, "onload", this.handleLoad);
- smallImg._isSmall = true;
- smallImg._src = smallUrl;
- smallImg.src = smallUrl;
- }
- // It's important that the large url's src is set after the small image
- // to ensure it's loaded second.
- largeImg.src = largeUrl;
- },
- handleLoad: function(evt){
- // summary:
- // Handles the loading of an image, both the large and small
- // versions. A render is triggered as a result of each image load.
- var img = evt.target;
- img._loaded = true;
- dojo.disconnect(img._conn);
- var type = img._type;
- switch(type){
- case "center":
- this.zoomCenterX = img.width / 2;
- this.zoomCenterY = img.height / 2;
- break;
- }
- var height = img.height;
- var width = img.width;
- if(width / this.size.w < height / this.size.h){
- // Fit the height to the height of the canvas
- img._baseHeight = this.canvas.height;
- img._baseWidth = width / (height / this.size.h);
- }else{
- // Fix the width to the width of the canvas
- img._baseWidth = this.canvas.width;
- img._baseHeight = height / (width / this.size.w);
- }
- img._centerX = width / 2;
- img._centerY = height / 2;
- this.render();
- this.onLoad(img._type, img._src, img._isSmall);
- },
- onLoad: function(type, url, isSmall){
- // summary:
- // Dummy function that is called whenever an image loads.
- // type: String
- // The position of the image that has loaded, either
- // "center", "left" or "right"
- // url: String
- // The src of the image
- // isSmall: Boolean
- // True if it is a small version of the image that has loaded,
- // false otherwise.
- }
- });
- });
|