<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>HTML5 Canvas 图片3D波动网状纹理映射特效</title>
<style>
body {
margin: 0;
}
#wrapper {
position: absolute;
width: 100%;
height: 100%;
overflow: hidden;
}
canvas {
background: #000;
}
</style>
</head>
<body>
<script src="js/jquery-1.11.1.min.js"></script>
<div id="wrapper">
<canvas></canvas>
</div>
<script>
$(function() {
var stageWidth;
var stageHeight;
var context = $("canvas")[0].getContext('2d');
var objectList;
var timeout;
var isMouseOnStage = false;
var mouseX = 0;
var mouseY = 0;
var cellXCount;
var cellYCount;
var image;
var imageScale;
var prms = {
src: "images/model riding horse.png",
fps: 60,
objectDistance: 40,
objectRotationV: Math.PI * .01,
objectRadius: 10,
animationDelta: 0.005,
animationSpringK: 0.03,
animationFriction: 0.25,
mouseForce: 100
}
$("#wrapper").bind("mousemove", function(e) {
mouseX = e.clientX;
mouseY = e.clientY;
isMouseOnStage = true;
}).bind("mouseleave", function(e) {
isMouseOnStage = false;
});
image = new Image();
image.onload = function() {
$(window).resize(reset);
reset();
}
image.src = prms.src;
function reset() {
stageWidth = $("#wrapper").width();
stageHeight = $("#wrapper").height();
$("#wrapper canvas").attr("width", stageWidth);
$("#wrapper canvas").attr("height", stageHeight);
objectList = [];
var x;
var y;
imageScale = Math.min(stageWidth / image.width, stageHeight / image.height);
cellXCount = Math.floor(image.width * imageScale / prms.objectDistance);
cellYCount = Math.floor(image.height * imageScale / prms.objectDistance);
x = (stageWidth - cellXCount * prms.objectDistance) / 2;
for (var xIndex = 0; xIndex < cellXCount; xIndex++) {
y = (stageHeight - cellYCount * prms.objectDistance) / 2;
for (var yIndex = 0; yIndex < cellYCount; yIndex++) {
var xDistFromCenter = x - stageWidth * .5;
var yDistFromCenter = y - stageHeight * .5;
var distFromCenter =
Math.sqrt(xDistFromCenter * xDistFromCenter) + Math.sqrt(yDistFromCenter * yDistFromCenter);
var circleRotation = Math.PI * prms.animationDelta * distFromCenter;
objectList.push(
new Circle(
x,
y,
circleRotation // _rotationV
)
);
y += prms.objectDistance;
}
x += prms.objectDistance;
}
var tempCell;
var tempIndex;
for (xIndex = 0; xIndex < cellXCount; xIndex++) {
for (yIndex = 0; yIndex < cellYCount; yIndex++) {
tempIndex = xIndex * cellYCount + yIndex;
tempCell = objectList[tempIndex];
if (xIndex > 0) {
tempIndex = (xIndex - 1) * cellYCount + yIndex;
tempCell.leftCircle = objectList[tempIndex];
}
if (xIndex < cellXCount - 1) {
tempIndex = (xIndex + 1) * cellYCount + yIndex;
tempCell.rightCircle = objectList[tempIndex];
}
if (yIndex > 0) {
tempIndex = xIndex * cellYCount + yIndex - 1;
tempCell.topCircle = objectList[tempIndex];
}
if (yIndex < cellYCount - 1) {
tempIndex = xIndex * cellYCount + yIndex + 1;
tempCell.bottomCircle = objectList[tempIndex];
}
}
}
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(onEnterFrame, 1000 / prms.fps);
}
function Circle(_x, _y, _rotation) {
this.x = _x;
this.y = _y;
this.vx = 0;
this.vy = 0;
this.ax = 0;
this.ay = 0;
this.rotation = _rotation;
this.cx = _x;
this.cy = _y;
this.rightCircle = null;
this.leftCircle = null;
this.topCircle = null;
this.bottomCircle = null;
}
Circle.prototype.rotationV = prms.objectRotationV;
Circle.prototype.radius = prms.objectRadius;
Circle.prototype.tick = function() {
this.rotation += this.rotationV;
var _x = this.radius * Math.cos(this.rotation) + this.cx;
var _y = this.radius * Math.sin(this.rotation) + this.cy;
var springK = prms.animationSpringK;
var friction = prms.animationFriction;
var mouseF = prms.mouseForce;
if (isMouseOnStage) {
var distX = this.x - mouseX;
var distY = this.y - mouseY;
var dist = Math.sqrt(distX * distX + distY * distY);
var rad = Math.atan2(distY, distX);
this.ax = mouseF / dist * Math.cos(rad);
this.ay = mouseF / dist * Math.sin(rad);
} else {
this.ax = 0;
this.ay = 0;
}
this.ax += -(this.x - _x) * springK - friction * this.vx;
this.ay += -(this.y - _y) * springK - friction * this.vy;
this.vx += this.ax;
this.vy += this.ay;
this.x += this.vx;
this.y += this.vy;
if (this.x > this.cx + prms.objectDistance) {
this.x = this.cx + prms.objectDistance;
} else if (this.x < this.cx - prms.objectDistance) {
this.x = this.cx - prms.objectDistance;
}
if (this.y > this.cy + prms.objectDistance) {
this.y = this.cy + prms.objectDistance;
} else if (this.y < this.cy - prms.objectDistance) {
this.y = this.cy - prms.objectDistance;
}
}
function onEnterFrame() {
var len = objectList.length;
var object;
while (len > 0) {
len -= 1;
object = objectList[len];
object.tick();
}
context.clearRect(0, 0, stageWidth, stageHeight);
var tempCell;
context.strokeStyle = 'rgba(255,255,255,.12)';
var imgCellW = image.width / cellXCount;
for (var x = 0; x < cellXCount; x++) {
for (var y = 0; y < cellYCount; y++) {
idx = x * cellYCount + y;
tempCell = objectList[idx];
if (tempCell.rightCircle && tempCell.bottomCircle) {
context.beginPath();
var rCell = tempCell.rightCircle;
var bCell = tempCell.bottomCircle;
var rbCell = tempCell.rightCircle.bottomCircle;
textureMap(context, image, [{
x: tempCell.x,
y: tempCell.y,
u: imgCellW * x,
v: imgCellW * y
}, {
x: rCell.x,
y: rCell.y,
u: imgCellW * (x + 1),
v: imgCellW * y
}, {
x: rbCell.x,
y: rbCell.y,
u: imgCellW * (x + 1),
v: imgCellW * (y + 1)
}, {
x: bCell.x,
y: bCell.y,