First thing is that to get rid of a problem, you should not hesitate to build intermediate layer to ease your work.
Here something like a 'Camera' Class could help you.
Other thing, your code to handle the scroll event is too complex. I know i repeat myself here, but i'd rather have this 19 lines long code :
var zoomSteps = [0.1, 0.2, 0.4, 0.6, 0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0];
var zoomIndex = zoomSteps.indexOf(1);
function doScroll(e) {
e = window.event || e;
var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
// increase zoom index by delta
var newZoomIndex = zoomIndex + delta;
// return if out of bounds
if (newZoomIndex < 0 || newZoomIndex >= zoomSteps.length) return;
// update previous scale
previousScale = scale;
// we have a new valid zoomIndex
zoomIndex = newZoomIndex;
// check we did not reach a boundary
zoomIsMin = (zoomIndex == 0);
zoomIsMax = (zoomIndex == zoomSteps.length - 1);
// compute new scale / image size
scale = zoomSteps[zoomIndex];
imageWidthZoomed = imageWidth * scale;
imageHeightZoomed = imageHeight * scale;
rather than this code doing just the same thing in 140 uncommented lines, which is just 7 times bigger (or 12 times if we remove the comments from my code) :
function doScroll(e) {
e = window.event || e;
var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
if (delta === 1) {
if (zoom < 5) {
zoom++;
zoomIsMax = false;
} else {
zoomIsMax = true;
}
} else if (delta === -1) {
if (zoom > -5) {
zoom--;
zoomIsMin = false;
} else {
zoomIsMin = true;
}
}
if (zoom === 1) {
if (delta === 1) {
previousScale = 1;
} else {
previousScale = 1.4;
}
scale = 1.2;
imageWidthZoomed = imageWidth * 1.2;
imageHeightZoomed = imageHeight * 1.2;
} else if (zoom === 2) {
if (delta === 1) {
previousScale = 1.2;
} else {
previousScale = 1.6;
}
scale = 1.4;
imageWidthZoomed = imageWidth * 1.4;
imageHeightZoomed = imageHeight * 1.4;
} else if (zoom === 3) {
if (delta === 1) {
previousScale = 1.4;
} else {
previousScale = 1.8;
}
scale = 1.6;
imageWidthZoomed = imageWidth * 1.6;
imageHeightZoomed = imageHeight * 1.6;
} else if (zoom === 4) {
if (delta === 1) {
previousScale = 1.6;
} else {
previousScale = 2;
}
scale = 1.8;
imageWidthZoomed = imageWidth * 1.8;
imageHeightZoomed = imageHeight * 1.8;
} else if (zoom === 5) {
if (delta === 1) {
previousScale = 1.8;
} else {
//out of range
}
scale = 2;
imageWidthZoomed = imageWidth * 2;
imageHeightZoomed = imageHeight * 2;
} else if (zoom === 0) {
if (delta === 1) {
previousScale = 0.8;
} else {
previousScale = 1.2;
}
scale = 1;
imageWidthZoomed = imageWidth;
imageHeightZoomed = imageHeight;
} else if (zoom === -1) {
if (delta === 1) {
previousScale = 0.6;
} else {
previousScale = 1;
}
scale = 0.8;
imageWidthZoomed = imageWidth * 0.8;
imageHeightZoomed = imageHeight * 0.8;
} else if (zoom === -2) {
if (delta === 1) {
previousScale = 0.4;
} else {
previousScale = 0.8;
}
scale = 0.6;
imageWidthZoomed = imageWidth * 0.6;
imageHeightZoomed = imageHeight * 0.6;
} else if (zoom === -3) {
if (delta === 1) {
previousScale = 0.2;
} else {
previousScale = 0.6;
}
scale = 0.4;
imageWidthZoomed = imageWidth * 0.4;
imageHeightZoomed = imageHeight * 0.4;
} else if (zoom === -4) {
if (delta === 1) {
previousScale = 0.1;
} else {
previousScale = 0.4;
}
scale = 0.2;
imageWidthZoomed = imageWidth * 0.2;
imageHeightZoomed = imageHeight * 0.2;
} else if (zoom === -5) {
if (delta === 1) {
//out of range
} else {
previousScale = 0.2;
}
scale = 0.1;
imageWidthZoomed = imageWidth * 0.1;
imageHeightZoomed = imageHeight * 0.1;
}
The end of doScroll would definitively benefit from the use of an intermediate Camera Class.
Now for your transition, idea is the following : rather than draw on zoom change, you just record that an update is required, with its parameters :
currentDrawParameters = [canvasPaint, paskutinisX, paskutinisY, imageWidthZoomed, imageHeightZoomed];
lastChangeTime = Date.now();
then you have a separate timer that will update the canvas if need be, and why not with an easing function ( meaning : a function [0.0;1.0] -> [0.0;1.0] ).
var currentDrawParameters = null;
var transitionTime = 500;
var lastChangeTime = -1;
var easingFunction = function (x) {
return Math.sqrt(x); // try : x , x*x, 0.2+0.8*x, ...
};
function drawSmoothly() {
// return if no need to draw
if (!currentDrawParameters) return;
var timeElapsed = Date.now() - lastChangeTime;
// return if transition ended
if (timeElapsed > transitionTime) {
currentDrawParameters = null;
return;
}
// compute time ratio = % time elapsed vs transitionTime
var ratio = timeElapsed / transitionTime;
// ease the ratio
var easedRatio = easingFunction(ratio);
contextBg.save();
contextBg.globalAlpha = 0.1;
contextBg.fillStyle = '#000';
// erase previous image progressively
contextBg.fillRect(0, 0, windowWidth, windowHeightUsed);
// draw the image with an opacity 0.0 ===>>> 1.0
contextBg.globalAlpha = easedRatio;
contextBg.drawImage.apply(contextBg, currentDrawParameters);
contextBg.restore();
}
setInterval(drawSmoothly, 50);
fiddle is here :
http://jsfiddle.net/gamealchemist/cDXj3/3/
try several easing function / time setting.