/*工作原理如下:
当拖动鼠标时,程序捕获放大镜最小外接矩形范围内的像素,然后程序将剪辑区域设置为放大镜所在范围,并将刚捕获的图像绘制到canvas自身,在绘制时调用接受9个参数的drawImage()方法来放大图像。
除了绘制被放大镜放在的图像外,还会擦除用户拖动之前的那个放大镜图像,每当拖动放大镜时,程序会调用putInageData()方法把上一次移动鼠标时用getImageData()所捕获的背景图像重新恢复到canvas中。所以每次拖动放大镜时:
【1】调用putInageData(),将上一次放大镜所在位置的背景图像恢复到canvas中
【2】调用getImageData(),捕获放大镜当前位置下面的像素数据
【3】将剪辑区域设置为放大镜所在范围
【4】绘制放大镜的镜片
*/
var canvas = document.getElementById('canvas'),
context = canvas.getContext('2d'),
image = new Image(),
imageData = null,
dragging = false,
glassSizeCanvas = document.getElementById('glassSizeCanvas'),
glassSizeContext = glassSizeCanvas.getContext('2d'),
MAXIMUM_SCALE = 4.0,
scaleOutput = document.getElementById('scaleOutput'),
magnifyingGlassRadius = 120,
magnificationScale = scaleOutput.innerHTML,
magnifyRectangle = {},
MAX_GLASS_RADIUS = 350,
magnifyingGlassX = 512,
magnifyingGlassY = 340,
magnifyZoomSlider = new COREHTML5.Slider('navy',
'rgb(80, 140, 230)',
0.25, // knob percent
90, // take up % of width
55), // take up % of height
glassSlider = new COREHTML5.Slider('navy', 'rgb(80, 140, 230)', 0.50, 90, 55),
animating = false,
animationLoop = null,
mousedown = null,
mouseup = null,
canvasRatio = canvas.height / canvas.width,
pinchRatio;
// Functions...................................................
function windowToCanvas(x, y) {
var bbox = canvas.getBoundingClientRect();
return { x: x - bbox.left * (canvas.width / bbox.width),
y: y - bbox.top * (canvas.height / bbox.height)
};
};
function calculateMagnifyRectangle(mouse) {
var top,
left,
bottom,
right;
magnifyRectangle.x = mouse.x - magnifyingGlassRadius;
magnifyRectangle.y = mouse.y - magnifyingGlassRadius;
magnifyRectangle.width = magnifyingGlassRadius*2 + 2*context.lineWidth;
magnifyRectangle.height = magnifyingGlassRadius*2 + 2*context.lineWidth;
top = magnifyRectangle.y;
left = magnifyRectangle.x;
bottom = magnifyRectangle.y + magnifyRectangle.height;
right = magnifyRectangle.x + magnifyRectangle.width;
if (left < 0) {
magnifyRectangle.width += left;
magnifyRectangle.x = 0;
}
else if (right > canvas.width) {
magnifyRectangle.width -= right - canvas.width;
}
if (top < 0) {
magnifyRectangle.height += magnifyRectangle.y;
magnifyRectangle.y = 0;
}
else if (bottom > canvas.height) {
magnifyRectangle.height -= bottom - canvas.height;
}
}
function setClip() {
context.beginPath();
context.arc(magnifyingGlassX, magnifyingGlassY,magnifyingGlassRadius, 0, Math.PI*2, false);
context.clip();
}
function drawMagnifyingGlassCircle(mouse) {
var gradientThickness = this.magnifyingGlassRadius / 7;
gradientThickness = gradientThickness < 10 ? 10 : gradientThickness;
gradientThickness = gradientThickness > 40 ? 40 : gradientThickness;
gradientThickness = 10;
this.context.save();
this.context.lineWidth = gradientThickness;
this.context.strokeStyle = 'rgb(0, 0, 255, 0.3)';
this.context.beginPath();
this.context.arc(mouse.x, mouse.y,
this.magnifyingGlassRadius, 0, Math.PI*2, false);
this.context.clip();
var gradient = this.context.createRadialGradient(
mouse.x, mouse.y, this.magnifyingGlassRadius-gradientThickness,
mouse.x, mouse.y, this.magnifyingGlassRadius);
gradient.addColorStop(0, 'rgba(0,0,0,0.2)');
gradient.addColorStop(0.80, 'rgb(235,237,255)');
gradient.addColorStop(0.90, 'rgb(235,237,255)');
gradient.addColorStop(1.0, 'rgba(150,150,150,0.9)');
this.context.shadowColor = 'rgba(52, 72, 35, 1.0)';
this.context.shadowOffsetX = 2;
this.context.shadowOffsetY = 2;
this.context.shadowBlur = 20;
this.context.strokeStyle = gradient;
this.context.stroke();
this.context.beginPath();
this.context.arc(mouse.x, mouse.y,
this.magnifyingGlassRadius-gradientThickness/2, 0, Math.PI*2, false);
this.context.clip();
this.context.lineWidth = gradientThickness;
this.context.strokeStyle = 'rgba(0,0,0,0.06)';
this.context.stroke();
this.context.restore();
};
function drawMagnifyingGlass(mouse) {
var scaledMagnifyRectangle;
if (window.netscape && netscape.security.PrivilegeManager)
netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
magnifyingGlassX = mouse.x;
magnifyingGlassY = mouse.y;
calculateMagnifyRectangle(mouse);
imageData = context.getImageData(magnifyRectangle.x,
magnifyRectangle.y,
magnifyRectangle.width,
magnifyRectangle.height);
context.save();
scaledMagnifyRectangle = {
width: magnifyRectangle.width * magnificationScale,
height: magnifyRectangle.height * magnificationScale
};
setClip();
context.drawImage(canvas,
magnifyRectangle.x, magnifyRectangle.y,
magnifyRectangle.width, magnifyRectangle.height,
magnifyRectangle.x + magnifyRectangle.width/2 -
scaledMagnifyRectangle.width/2,
magnifyRectangle.y + magnifyRectangle.height/2 -
scaledMagnifyRectangle.height/2,
scaledMagnifyRectangle.width,
scaledMagnifyRectangle.height);
context.restore();
drawMagnifyingGlassCircle(mouse);
}
function eraseMagnifyingGlass() { // Called when the mouse moves
if (imageData != null) {
context.putImageData(imageData,
magnifyRectangle.x,
magnifyRectangle.y);
}
}
function drawGlassIcon(context, radius) {
context.save();
context.clearRect(0,0,context.canvas.width,
context.canvas.height);
context.shadowColor = 'rgba(52, 72, 35, 0.5)';
context.shadowOffsetX = 1;
context.shadowOffsetY = 1;
context.shadowBlur = 2;
context.beginPath();
context.translate(context.canvas.width/2,
context.canvas.height/2);
context.beginPath();
context.lineWidth = 1.5;
context.arc(0, 0, radius+3, 0, Math.PI*2, false);
context.strokeStyle = 'rgb(52, 72, 35)';
context.stroke();
context.beginPath();
context.lineWidth = 0.5;
context.strokeStyle = 'rgba(255,255,255,0.6)';
context.arc(0, 0, radius+6, 0, Math.PI*2, false);
context.stroke();
context.restore();
};
function drawMagnificationText(value, percent) {
scaleOutput.innerHTML = value;
percent = percent < 0.35 ? 0.35 : percent;
scaleOutput.style.fontSize = percent*MAXIMUM_SCALE/2 + 'em';
}
function updateMagnifyingGlass() {
eraseMagnifyingGlass();
drawMagnifyingGlass({ x: magnifyingGlassX, y: magnifyingGlassY });
}
function step(time, lastTime, mouse, speed) {
var elapsedTime = time - lastTime,
nextLeft = mouse.x - magnifyingGlassRadius + speed.vx*(elapsedTime/10),
nextTop = mouse.y - magnifyingGlassRadius + speed.vy*(elapsedTime/10),
nextRight = nextLeft + magnifyingGlassRadius*2,
nextBottom = nextTop + magnifyingGlassRadius*2;
eraseMagnifyingGlass();
if (nextLeft < 0) {
speed.vx = -speed.vx;
mouse.x = magnifyingGlassRadius;
}
else if (nextRight > canvas.width) {
speed.vx = -speed.vx;
mouse.x = canvas.width - magnifyingGlassRadius;
}
if (nextTop < 0) {
speed.vy = -speed.vy;
mouse.y = magnifyingGlassRadius;
}
else if (nextBottom > canvas.height) {
speed.vy = -speed.vy;
mouse.y = canvas.height - magnifyingGlassRadius;
}
mouse.x += speed.vx*(elapsedTime/10);
mouse.y += speed.vy*(elapsedTime/10);
drawMagnifyingGlass(mouse);
}
function animate(mouse, speed) {
var time, lastTime = 0, elapsedTime;
animating = true;
if (lastTime === 0) {
lastTime = +new Date;
}
animationLoop = setInterval(function() {
var time = + new Date;
step(time, lastTime, mouse, speed);
lastTime = time;
}, 1000/60);
}
function didThrow() {
var elapsedTime = mouseup.time - mousedown.time;
var elapsedMotion = Math.abs(mouseup.x - mousedown.x) +
Math.abs(mouseup.y - mousedown.y);
return false; //(elapsedMotion / elapsedTime * 10) > 3;
}
// Touch Event Handlers........................................
function isPinching (e) {
var changed = e.changedTouches.length,
touching = e.touches.length;
return changed === 1 || changed === 2 && touching === 2;
}
function isDragging (e) {
var changed = e.changedTouches.length,
touching = e.touches.length;
return changed === 1 && touching === 1;
}
canvas.ontouchstart = function (e) {
var changed = e.changedTouches.length,
touching = e.touches.length;
e.preventDefault(e);
if (isDragging(e)) {
mouseDownOrTouchStart(windowToCanvas(e.pageX, e.pageY));
}
else if (isPinching(e)) {
var touch1 = e.touches.item(0),
touch2 = e.touches.item(1),
point1 = windowToCanvas(touch1.pageX, touch1.pageY),
point2 = windowToCanvas(touch2.pageX, touch2.pageY);
distance = Math.sqrt(Math.pow(point2.x - point1.x, 2) + Math.pow(point2.x - point1.x, 2));
pinchRatio = magnificationScale / distance;
}
};
canvas.ontouchmove = function (e) {
var changed = e.changedTouches.length,
touching = e.touches.length,
distance, touch1, touch2;
e.preventDefault(e);
if (isDragging(e)) {
mouseMoveOrTouchMove(windowToCanvas(e.pageX, e.pageY));
}
else if (isPinching(e)) {
var touch1 = e.touches.item(0),
touch2 = e.touches.item(1),
point1 = windowToCanvas(touch1.pageX, touch1.pageY),
point2 = windowToCanvas(touch2.pageX, touch2.pageY),
scale;
distance = Math.sqrt(Math.pow(point2.x - point1.x, 2) + Math.pow(point2.x - point1.x, 2));
scale = pinchRatio * distance;
if (scale > 1 && scale < 3) {
magnificationScale = parseFloat(pinchRatio * distance).toFixed(2);
draw();
}
}
};
canvas.ontouchend = function (e) {
e.preventDefault(e);
mouseUpOrTouchEnd(windowToCanvas(e.pageX, e.pageY));
};
// Mouse Event Handlers........................................
canvas.onmousedown = function (e) {
e.preventDefault(e);
mouseDownOrTouchStart(windowToCanvas(e.clientX, e.clientY));
};
canvas.onmousemove = function (e) {
e.preventDefault(e);
mouseMoveOrTouchMove(windowToCanvas(e.clientX, e.clientY));
};
canvas.onmouseup = function (e) {
e.preventDefault(e);
mouseUpOrTouchEnd(windowToCanvas(e.clientX, e.clientY));
};
function mouseDownOrTouchStart(mouse) {
mousedown = { x: mouse.x, y: mouse.y, time: (new Date).getTime() };
if (animating) {
animating = false;
clearInterval(animationLoop);
eraseMagnifyingGlass();
}
else {
dragging = true;
context.save();
}
};
function mouseMoveOrTouchMove(mouse) {
if (dragging) {
eraseMagnifyingGlass();
drawMagnifyingGlass(mouse);
}
};
function mouseUpOrTouchEnd(mouse) {
mouseup = { x: mouse.x, y: mouse.y, time: (new Date).getTime() };
if (dragging) {
if (didThrow()) {
velocityX = (mouseup.x-mousedown.x)/100;
velocityY = (mouseup.y-mousedown.y)/100;
animate(mouse, { vx: velocityX, vy: velocityY });
}
else {
//eraseMagnifyingGlass();
}
}
dragging = false;
};
// Slider Event Handlers.......................................
magnifyZoomSlider.addChangeListener( function(e) {
var maxRadius = (glassSizeCanvas.width/2-7);
percent = magnifyZoomSlider.knobPercent,
value = parseFloat(1 + percent * 2).toFixed(2);
drawMagnificationText(value, percent);
magnificationScale = value;
updateMagnifyingGlass();
});
glassSlider.addChangeListener( function(e) {
var maxRadius = glassSizeCanvas.width/2-5,
percent = parseFloat(glassSlider.knobPercent),
value = 25 + new Number((percent * 175).toFixed(0));
magnifyingGlassRadius = value
drawGlassIcon(glassSizeContext, maxRadius * percent);
updateMagnifyingGlass();
});
// Initialization..............................................
context.fillStyle = 'cornflowerblue';
context.strokeStyle = 'rgba(250, 250, 0, 0.5)';
context.shadowColor = 'rgba(0, 0, 0, 0.5)';
context.shadowOffsetX = 10;
context.shadowOffsetY = 10;
context.shadowBlur = 20;
function draw() {
var maxRadius = (glassSizeCanvas.width/2-7),
percent = parseFloat(glassSlider.knobPercent);
context.drawImage(image, 0, 0, canvas.width, canvas.height);
drawGlassIcon(glassSizeContext, maxRadius * 0.5);
drawMagnificationText(magnificationScale, percent);
drawMagnifyingGlass({ x: magnifyingGlassX, y: magnifyingGlassY });
}
image.src = '../../shared/images/camp.png';
image.onload = function(e) {
draw();
};
drawGlassIcon(glassSizeContext, (glassSizeCanvas.width/2-7)/2 );
canvas.addEventListener('dragenter', function (e) {
e.preventDefault();
e.dataTransfer.effectAllowed = 'copy';
}, false);
canvas.addEventListener('dragover', function (e) {
e.preventDefault();
}, false);
window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
canvas.addEventListener('drop', function (e) {
var file = e.dataTransfer.files[0];
window.requestFileSystem(window.TEMPORARY, 5*1024*1024,
function (fs) {
fs.root.getFile(file.name, {create: true},
function (fileEntry) {
fileEntry.createWriter( function (writer) {
writer.write(file);
});
image.src = fileEntry.toURL();
},
function (e) {
alert(e.code);
}
);
},
function (e) {
alert(e.code);
}
);
}, false);
magnifyZoomSlider.appendTo('magnificationSliderDiv');
glassSlider.appendTo('glassSizeSliderDiv');
magnifyZoomSlider.draw();
glassSlider.draw();
//..................................................................
<body id='body'>
<div id='backdrop'>
<div id='controls'>
<span id='scaleOutput'>1.5</span>
<div id='magnificationSliderDiv' class='slider'></div>
<canvas id='glassSizeCanvas' width='40' height='40'>
Canvas not supported
</canvas>
<div id='glassSizeSliderDiv' class='slider'></div>
</div>
<canvas id='canvas' width='955' height='611'>
Canvas not supported
</canvas>
</div>
<script src='../../shared/js/roundedRectangle.js'></script>
<script src='../../shared/js/slider.js'></script>
<script src='example.js'></script>
</body>
</html>