为了测试IE+ASV3.03, FF1.5和OPERA9.02的图形处理速度,编写了一个拼图的小游戏。
有几个代码说明如下:
1,对象模型:
1 0 cx 1 0 rx cos -sin 0 1 0 -rx a c e
0 1 cy 0 1 ry sin cos 0 0 1 -ry b d f
0 0 1 0 0 1 0 0 1 0 0 1 0 0 1
=
a*cos-b*sin c*cos-d*sin e*cos-f*sin-rx*cos+ry*sin+rx+cx
a*sin+b*cos c*sin+d*cos e*sin+f*cos-rx*sin-ry*cos+ry+cy
0 0 1
在ImageObject.updateImage(cx, cy, rotateFlag, rx, ry)中使用了该公式,其中cx, cy为移动增量,rx, ry为旋转中心。
3,Block对象的边形状有三种,0: 直线 1:在1/3处圆弧 2:在2/3处圆弧。有兴趣可以自行添加。相邻的Block边必须形状吻合,在createBlockList
中保证了这一点。
4,Region对象中getOutline和getPathStr的区别是前者仅仅返回外形路径,用于指示选中的对象,而后者用于ClipPath
5,判断图形是否合并的条件:角度相同,位移点的距离在一定范围内(MIN_MERGE_DISTANCE),是相邻的块(Block)
其它的代码比较简单,就不进行说明了。可以修改IMAGE_SOURCE,imageRow, imageCol以改变图形和分隔的行列数。
测试结果为 ASV>OPERA>FF,在10×10的情况下,只有ASV勉强可用。
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:ev="http://www.w3.org/2001/xml-events"
width="100%" height="100%" shape-rendering="optimizeSpeed" zoomAndPan="enable" onload="init(evt)" onmousedown="grab(evt)" onmousemove="drag(evt)" onmouseup="drop(evt)">
<title>Puzzle</title>
<desc>Puzzle</desc>
<script language="JavaScript" >
<![CDATA[
/*
2006-10-21
Wang Zhao Yan
EMail: [email protected]
BLOG: http://blog.csdn.net/firefight/
Beijing
P.R.China
Test on: IE6+ASV3 FF1.5 OPERA9.02
Usage: Grab and drag image by mouse left button.
Click mouse left button to rotate the image.
Code description
----------------
1, Picture is seperated to blocks at first, each block has column and row number, and border path type. Application should in
charge of the border path setting. For example, neighbours should have same kind of border path.
2. Regions consist of blocks, it can be merge into other regions by move all its blocks to other one. It is in charge of
generate path string for image object.
3. Image object is a SVG group consist of a <image> and a <clipPath> with <path> setting.
One image object looks like:
<g id="group" transform="matrix(a,b,c,d,e,f)" display="none">
<clipPath id="clip"><path id="path" d="M 0 0"/></clipPath>
<image id="image" width="400" height="400" xlink:href="1.jpg" clip-path="url(#clip)"/>
</g>
4, Add mouse event to SVG canvas, including mouse down(grab), mouse up(drop) and mouse move(drag).
Move or rotate will affect group transform string, keep clip-path and image relative fixed.
Click on image object will "grab" it, keep mouse down and move will "drag" image, release mouse will "drop" image to a
new position.
5, In order to improve grap and drag speed, after select one image, swap svg node to bring selected image to then front.
(append selected one in SVG node proved to be very slowly);
6, After drop, image object will test it neighbour to determine which one should be merged toghther. Drop object will test
object borders in other group, if distance less than minimize value, drop object will be added to target group. Pay attention to
the check distance function, group transform matrix should be consider.
border : neighbour, according to the blocks region hold
translate : distance less than mindistance (arrording to translate offset, remember complete picture's all offset is equal, only clip offset is different)
rotate angle : equal, only regions with same direction can be merge together
loop1: Search other image objects which meet condition 2 and 3
loop2: Search whether image object contain border block for each blocks in this region,
Once got one then break and return,
otherwise continue loop till finish iteration.
7, If no swap and no merge happen, and move distance less than min rotate distance, image will rotate round mouse point.
8, If merge group happened and after then target group contains "imageRow * imageCol" images, it indicate all works done, move group to origin
and show "Game over!".
*/
var svgNS = "http://www.w3.org/2000/svg";
var xlinkNS="http://www.w3.org/1999/xlink";
var svgDocument = null;
var svgRoot = null;
var canvas = null;
var background = null; //to receive mouse event on drag
var dragImage = null;
var imageList = new Array();
var trueCoords = null;
var grabPoint = null;
var orgPoint = null;
var swapFlag = false; //Swap happen or not
//In order to keep all points is integer, imageWidth will be round to imageCol * 4 and imageHeight round to imageRow * 4
//For example, if imageCol = 3, 400%(3*4)=4, imageWidth will be round to 400-4=396.
var imageWidth = 400;
var imageHeight = 400;
var imageRow = 3; //You can change from Row and column number from 1 to 10
var imageCol = 3;
var rowUnit; //Height of each row
var colUnit; //Width of each column
var MIN_MERGE_DISTANCE = 5; //Merge distance
var MIN_ROTATE_DISTANCE = 2; //Rotate distance
var CANVAS_ID = "canvas";
var BACKGROUND_ID = "background";
var IMAGE_SOURCE = "1.jpg"; //Source file
var OUTLINE_ID = "outline";
var OUTLINE_STROKE = "silver";
var OUTLINE_STROKE_WIDTH = 2;
function init(evt)
{
svgDocument = evt.target.ownerDocument;
svgRoot = svgDocument.documentElement;
//Resize image in order to round path point to int;
roundSize();
trueCoords = svgRoot.createSVGPoint();
grabPoint = svgRoot.createSVGPoint();
orgPoint = svgRoot.createSVGPoint();
//Get backgroup and canvas element
background = svgDocument.getElementById( BACKGROUND_ID );
canvas = svgDocument.getElementById( CANVAS_ID );
var blockList = createBlockList();
for(var i = 0; i < imageRow; i++)
{
for(var j = 0; j < imageCol; j++)
{
var id = "part_" + i + "_" + j;
var region = new Region();
region.addBlock( blockList[ i*imageCol + j ] );
var image = new ImageObject(id, CANVAS_ID, 0, region);
image.create();
image.updatePath();
imageList[imageList.length] = image;
//Random rotate and spread images
var cx = Math.random() * innerWidth / 2;
var cy = Math.random() * innerHeight / 2;
var rotateFlag = parseInt( Math.random() * 10 ) % 4;
var rx = j * colUnit + colUnit / 2;
var ry = i * rowUnit + rowUnit / 2;
image.updateImage(cx, cy, rotateFlag, rx, ry);
}
}
//Create outline
createOutline();
}
function createBlockList()
{
//Edge type current support
// 0: line; 1: one big circle; 2: one small circle
var edgeTypeNumber = 2;
//Horizontal edge [col][row+1] vertical [row][col+1]
//In order to simplize usage, we just use string to hold edge type
var hEdgesStr = "";
var vEdgesStr = "";
for(var i = 0; i < imageRow + 1; i++)
{
for(var j = 0; j < imageCol ; j++)
{
if( (i == 0) || ( i== imageRow ) )
hEdgesStr = hEdgesStr + "0";
else
hEdgesStr = hEdgesStr + (( parseInt( Math.random() * 10 ) ) % edgeTypeNumber + 1);
}
}
for(var i = 0; i < imageRow; i++)
{
for(var j = 0; j < imageCol + 1; j++)
{
if( (j == 0) || ( j == imageCol ) )
vEdgesStr = vEdgesStr + "0";
else
vEdgesStr = vEdgesStr + (( parseInt(Math.random() * 10 ) ) % edgeTypeNumber + 1);
}
}
//alert( hEdgesStr + "; " + vEdgesStr);
var blockList = new Array();
for(var i = 0; i < imageRow; i++)
{
for(var j = 0; j < imageCol; j++)
{
var block = new Block(i, j);
block.topEdge = parseInt( hEdgesStr.substr(i*imageCol + j, 1) );
block.bottomEdge = parseInt( hEdgesStr.substr( (i+1)*imageCol + j, 1) );
block.leftEdge = parseInt( vEdgesStr.substr(i* (imageCol + 1) + j, 1) );
block.rightEdge = parseInt( vEdgesStr.substr(i* (imageCol + 1) + j + 1, 1) );
//alert(i + " " + j + ": " + block.topEdge + " " + block.rightEdge + " " + block.bottomEdge + " " + block.leftEdge);
blockList[blockList.length] = block;
}
}
//alert(blockList.length);
return blockList;
}
function createOutline()
{
//Outline path
var groups = svgDocument.getElementById(CANVAS_ID);
if(groups != null)
{
var lastGroup = groups.lastChild;
var path = svgDocument.createElementNS(svgNS, "path");
path.setAttributeNS(null, "id", OUTLINE_ID);
path.setAttributeNS(null, "d", "M 0 0");
path.setAttributeNS(null, "fill", "none");
path.setAttributeNS(null, "stroke", OUTLINE_STROKE);
path.setAttributeNS(null, "stroke-width", OUTLINE_STROKE_WIDTH);
path.setAttributeNS(null, "point-events", "none");
lastGroup.appendChild(path);
}
}
function showOutline(pathStr)
{
var path = svgDocument.getElementById(OUTLINE_ID);
if(path != null)
{
path.setAttributeNS(null,"d", pathStr);
}
}
function roundSize()
{
if( imageRow > 10)
imageRow = 10;
if( imageCol > 10 )
imageCol = 10;
imageWidth = imageWidth - imageWidth % ( imageCol * 4 );
imageHeight = imageHeight - imageHeight % ( imageRow * 4 );
rowUnit = imageHeight / imageRow; //Row part unit
colUnit = imageWidth / imageCol; //Col part unit
}
function matrixToStr(matrix)
{
var transformStr = "matrix(" + matrix.a + "," + matrix.b + "," + matrix.c + ","
+ matrix.d + "," + matrix.e + "," + matrix.f + ")";
return transformStr;
}
//Get image object by SVG node id
function getImageObject(id)
{
var index = id.indexOf("_");
var str = id.substr(index+1);
index = str.indexOf("_");
var row = parseInt(str.substr(0,index));
var col = parseInt(str.substr(index+1));
//Search specific block in image object region
for(var i=0; i<imageList.length; i++)
{
if( (imageList[i] != null) && (imageList[i].region.hasBlock(row, col) ))
return imageList[i];
}
return null;
}
function getTrueCoords(evt)
{
var newScale = svgRoot.currentScale;
var translation = svgRoot.currentTranslate;
trueCoords.x = (evt.clientX - translation.x)/newScale;
trueCoords.y = (evt.clientY - translation.y)/newScale;
}
//Swap selected SVG node to last one
function swapImage(id1, id2)
{
var group1, clip1, path1, image1;
var group2, clip2, path2, image2;
var group1Id = "group" + id1;
var clip1Id = "clip" + id1;
var path1Id = "path" + id1;
var image1Id = "image" + id1;
group1 = svgDocument.getElementById(group1Id);
clip1 = svgDocument.getElementById(clip1Id);
path1 = svgDocument.getElementById(path1Id);
image1 = svgDocument.getElementById(image1Id);
var group2Id = "group" + id2;
var clip2Id = "clip" + id2;
var path2Id = "path" + id2;
var image2Id = "image" + id2;
group2 = svgDocument.getElementById(group2Id);
clip2 = svgDocument.getElementById(clip2Id);
path2 = svgDocument.getElementById(path2Id);
image2 = svgDocument.getElementById(image2Id);
swapNodeAttribute(group1, group2, "id");
swapNodeAttribute(group1, group2, "transform");
swapNodeAttribute(clip1, clip2, "id");
swapNodeAttribute(path1, path2, "id");
swapNodeAttribute(path1, path2, "d");
swapNodeAttribute(image1, image2, "id");
swapNodeAttribute(image1, image2, "clip-path");
}
function swapNodeAttribute(node1, node2, attrName)
{
var value1 = node1.getAttributeNS(null, attrName);
var value2 = node2.getAttributeNS(null, attrName);
node1.setAttributeNS(null, attrName, value2);
node2.setAttributeNS(null, attrName, value1);
}
//Search neighbours in image list
function getNeighbourImage()
{
for(var i=0; i<imageList.length; i++)
{
if( (dragImage == imageList[i]) || (imageList[i] == null) )
continue;
if(dragImage.checkNeighbour(imageList[i]) == true)
{
//Return neighbour and remove it from image list
var newImage = imageList[i];
imageList[i] = null;
return newImage;
}
}
return null;
}
function gameOver()
{
alert("Game Over!");
}
///
/// Block and Region class
///
function Block(row, col)
{
//Data
this.row = row;
this.col = col;
this.topEdge = 0;
this.rightEdge = 0;
this.bottomEdge = 0;
this.leftEdge = 0;
}
function Region()
{
//Data
this.blocks = new Array();
//Methods
this.addBlock = addBlock;
this.hasBlock = hasBlock; //Test contain one specific block or not
this.mergeRegion = mergeRegion;
this.getPathStr = getPathStr;
this.getOutline = getOutline;
this.isNeighbour = isNeighbour; //Test other region has neighbour block or not
}
function addBlock(block)
{
this.blocks[this.blocks.length] = block;
}
function hasBlock(row, col)
{
for(var i = 0; i<this.blocks.length; i++)
{
if( (this.blocks[i].row == row) && (this.blocks[i].col == col) )
return true;
}
return false;
}
function mergeRegion(newRegion)
{
if( (newRegion == null) || (newRegion.blocks == null) )
return;
//Copy all block from new region
for(var i = 0; i<newRegion.blocks.length; i++)
{
this.blocks[this.blocks.length] = newRegion.blocks[i];
}
}
function isNeighbour(newRegion)
{
if(newRegion.blocks == null)
return false;
var block1, block2;
for(var i = 0; i<this.blocks.length; i++)
{
block1 = this.blocks[i];
for(var j = 0; j<newRegion.blocks.length; j++)
{
block2 = newRegion.blocks[j];
//Same row, previous or next col is neighbour
if( (block1.row == block2.row) && (Math.abs(block1.col - block2.col) == 1) )
return true;
//Same col, previous or next row is neighbour
if( (block1.col == block2.col) && (Math.abs(block1.row - block2.row) == 1) )
return true;
}
}
return false;
}
function getOutline()
{
var pathStr = " ";
var x, y;
var rowPart = rowUnit / 4;
var colPart = colUnit / 4;
var topFlag = true;
var rightFlag = true
var bottomFlag = true;
var leftFlag = true;
for(var i = 0; i < this.blocks.length; i++)
{
var block = this.blocks[i];
//Set outline edge flag
if( (block.row > 0) && this.hasBlock(block.row - 1, block.col) )
topFlag = false;
if( (block.row < (imageRow - 1)) && this.hasBlock(block.row + 1, block.col) )
bottomFlag = false;
if( (block.col > 0) && this.hasBlock(block.row , block.col - 1) )
leftFlag = false;
if( (block.col < (imageCol - 1) ) && this.hasBlock(block.row , block.col + 1) )
rightFlag = false;
var ox = block.col * colUnit;
var oy = block.row * rowUnit;
if(topFlag)
{
//Move to begin point
x = ox;
y = oy;
pathStr = pathStr + "M " + x + " " + y + " ";
//Top edge, y fixed to oy
if(block.topEdge == 1)
{
x = ox + colPart;
pathStr = pathStr + " L " + x + " " + y + " a " + colPart / 2 + " " + colPart / 2 + " 0 1 1 " + colPart + " 0 L";
x = ox + colUnit;
pathStr = pathStr + x + " " + y + " ";
}
else if(block.topEdge == 2)
{
x = ox + colPart * 2;
pathStr = pathStr + " L " + x + " " + y + " a " + colPart / 2 + " " + colPart / 2 + " 0 1 1 " + colPart + " 0 L ";
x = ox + colUnit;
pathStr = pathStr + x + " " + y + " ";
}
else
{
x = ox + colUnit;
pathStr = pathStr + " L " + x + " " + y + " ";
}
}
if(rightFlag)
{
//Move to begin point
x = ox + colUnit;
y = oy;
pathStr = pathStr + "M " + x + " " + y + " ";
//Right edge, x fixed to ox + colUnit;
if(block.rightEdge == 1)
{
y = oy + rowPart;
pathStr = pathStr + " L " + x + " " + y + " a " + rowPart / 2 + " " + rowPart / 2 + " 0 1 1 0 " + rowPart + " L ";
y = oy + rowUnit;
pathStr = pathStr + x + " " + y + " ";
}
else if(block.rightEdge == 2)
{
y = oy + rowPart * 2;
pathStr = pathStr + " L " + x + " " + y + " a " + rowPart / 2 + " " + rowPart / 2 + " 0 1 1 0 " + rowPart + " L ";
y = oy + rowUnit;
pathStr = pathStr + x + " " + y + " ";
}
else
{
y = oy + rowUnit;
pathStr = pathStr + " L " + x + " " + y + " ";
}
}
if(bottomFlag)
{
//Move to begin point
x = ox + colUnit;
y = oy + rowUnit;
pathStr = pathStr + "M " + x + " " + y + " ";
//Bottom edge, y fixed to oy + rowUnit;
if(block.bottomEdge == 1)
{
x = ox + colUnit - 2 * colPart;
pathStr = pathStr + " L " + x + " " + y + " a " + colPart / 2 + " " + colPart / 2 + " 0 1 0 -" + colPart + " 0 L";
x = ox;
pathStr = pathStr + x + " " + y + " ";
}
else if(block.bottomEdge == 2)
{
x = ox + colUnit - colPart;
pathStr = pathStr + " L " + x + " " + y + " a " + colPart / 2 + " " + colPart / 2 + " 0 1 0 -" + colPart + " 0 L ";
x = ox;
pathStr = pathStr + x + " " + y + " ";
}
else
{
x = ox;
pathStr = pathStr + " L " + x + " " + y + " ";
}
}
if(leftFlag)
{
//Move to begin point
x = ox;
y = oy + rowUnit;
pathStr = pathStr + "M " + x + " " + y + " ";
//Left edge, x fixed to ox
if(block.leftEdge == 1)
{
y = oy + rowUnit - 2 * rowPart;
pathStr = pathStr + " L " + x + " " + y + " a " + rowPart / 2 + " " + rowPart / 2 + " 0 1 0 0 -" + rowPart + " L ";
y = oy;
pathStr = pathStr + x + " " + y + " ";
}
else if(block.leftEdge == 2)
{
y = oy + rowUnit - rowPart;
pathStr = pathStr + " L " + x + " " + y + " a " + rowPart / 2 + " " + rowPart / 2 + " 0 1 0 0 -" + rowPart + " L ";
y = oy;
pathStr = pathStr + x + " " + y + " ";
}
else
{
y = oy;
pathStr = pathStr + " L " + x + " " + oy + " ";
}
}
}
//alert(pathStr);
return pathStr;
}
function getPathStr()
{
var pathStr = " ";
var x, y;
var rowPart = rowUnit / 4;
var colPart = colUnit / 4;
for(var i = 0; i < this.blocks.length; i++)
{
var block = this.blocks[i];
pathStr = pathStr + "M ";
var ox = block.col * colUnit;
var oy = block.row * rowUnit;
//Move to begin point
pathStr = pathStr + ox + " " + oy + " ";
//Top edge, y fixed to oy
y = oy;
if(block.topEdge == 1)
{
x = ox + colPart;
pathStr = pathStr + " L " + x + " " + y + " a " + colPart / 2 + " " + colPart / 2 + " 0 1 1 " + colPart + " 0 L";
x = ox + colUnit;
pathStr = pathStr + x + " " + y + " ";
}
else if(block.topEdge == 2)
{
x = ox + colPart * 2;
pathStr = pathStr + " L " + x + " " + y + " a " + colPart / 2 + " " + colPart / 2 + " 0 1 1 " + colPart + " 0 L ";
x = ox + colUnit;
pathStr = pathStr + x + " " + y + " ";
}
else
{
x = ox + colUnit;
pathStr = pathStr + " L " + x + " " + y + " ";
}
//Right edge, x fixed to ox + colUnit;
x = ox + colUnit;
if(block.rightEdge == 1)
{
y = oy + rowPart;
pathStr = pathStr + " L " + x + " " + y + " a " + rowPart / 2 + " " + rowPart / 2 + " 0 1 1 0 " + rowPart + " L ";
y = oy + rowUnit;
pathStr = pathStr + x + " " + y + " ";
}
else if(block.rightEdge == 2)
{
y = oy + rowPart * 2;
pathStr = pathStr + " L " + x + " " + y + " a " + rowPart / 2 + " " + rowPart / 2 + " 0 1 1 0 " + rowPart + " L ";
y = oy + rowUnit;
pathStr = pathStr + x + " " + y + " ";
}
else
{
y = oy + rowUnit;
pathStr = pathStr + " L " + x + " " + y + " ";
}
//Bottom edge, y fixed to oy + rowUnit;
y = oy + rowUnit;
if(block.bottomEdge == 1)
{
x = ox + colUnit - 2 * colPart;
pathStr = pathStr + " L " + x + " " + y + " a " + colPart / 2 + " " + colPart / 2 + " 0 1 0 -" + colPart + " 0 L";
x = ox;
pathStr = pathStr + x + " " + y + " ";
}
else if(block.bottomEdge == 2)
{
x = ox + colUnit - colPart;
pathStr = pathStr + " L " + x + " " + y + " a " + colPart / 2 + " " + colPart / 2 + " 0 1 0 -" + colPart + " 0 L ";
x = ox;
pathStr = pathStr + x + " " + y + " ";
}
else
{
x = ox;
pathStr = pathStr + " L " + x + " " + y + " ";
}
//Left edge, x fixed to ox
x = ox;
if(block.leftEdge == 1)
{
y = oy + rowUnit - 2 * rowPart;
pathStr = pathStr + " L " + x + " " + y + " a " + rowPart / 2 + " " + rowPart / 2 + " 0 1 0 0 -" + rowPart + " L ";
y = oy;
pathStr = pathStr + x + " " + y + " ";
}
else if(block.leftEdge == 2)
{
y = oy + rowUnit - rowPart;
pathStr = pathStr + " L " + x + " " + y + " a " + rowPart / 2 + " " + rowPart / 2 + " 0 1 0 0 -" + rowPart + " L ";
y = oy;
pathStr = pathStr + x + " " + y + " ";
}
else
{
y = oy;
pathStr = pathStr + " L " + x + " " + oy + " ";
}
}
//alert(pathStr);
return pathStr;
}
///
/// ImageObject class begin
///
function ImageObject(id, parentId, angle, region)
{
//Data
this.id = id;
this.parentId = parentId;
this.angle = angle;
this.region = region;
//Methods
this.create = create; //Create SVG node and show
this.updateImage = updateImage; //Update position and rotate status
this.updatePath = updatePath; //Update clip path after merge with other images
this.hide = hide; //Hide svg node
this.deleteFromCanvas = deleteFromCanvas; //Delete svg node
this.enableEvent = enableEvent; //Enable/Disable svg mouse event
this.checkNeighbour = checkNeighbour; //Check image neighbour or not
this.mergeImage = mergeImage;
}
function deleteFromCanvas()
{
var groupId = "group" + this.id;
var parentGroup = svgDocument.getElementById(this.parentId);
var group = svgDocument.getElementById(groupId);
if ((parentGroup != null) && (group != null) )
parentGroup.removeChild(group);
}
function hide()
{
var groupId = "group" + this.id;
var group = svgDocument.getElementById(groupId);
if(group != null)
{
group.setAttributeNS(null, "display", "none");
}
}
function mergeImage(newImage)
{
if( (newImage == null) || (newImage.region == null))
return;
//Merge image clip region
this.region.mergeRegion(newImage.region);
newImage.enableEvent(false);
newImage.hide();
}
function create()
{
var parentGroup = svgDocument.getElementById(this.parentId);
var group, clip, path, image;
var groupId = "group" + this.id;
var clipId = "clip" + this.id;
var pathId = "path" + this.id;
var imageId = "image" + this.id;
group = svgDocument.getElementById(groupId);
if(group == null)
{
//Create object, including group, clip-path and image
group = svgDocument.createElementNS(svgNS, "g");
parentGroup.appendChild(group);
group.setAttributeNS(null, "id", groupId);
//group.setAttributeNS(null, "opacity", "0.8");
//Create clip and clip path
clip = svgDocument.createElementNS(svgNS, "clipPath");
clip.setAttributeNS(null,"id",clipId);
path = svgDocument.createElementNS(svgNS, "path");
path.setAttributeNS(null,"id",pathId);
//path.setAttributeNS(null,"fill","none");
//path.setAttributeNS(null,"stroke","none");
clip.appendChild(path);
//Create image
image = svgDocument.createElementNS(svgNS, "image");
image.setAttributeNS(null, "id", imageId);
image.setAttributeNS(null, "width", imageWidth);
image.setAttributeNS(null, "height", imageHeight);
image.setAttributeNS(null, "preserveAspectRatio", "none");
image.setAttributeNS(xlinkNS, "xlink:href", IMAGE_SOURCE);
var urlClip = "url(#" + clipId + ")";
image.setAttributeNS(null, "clip-path", urlClip);
//Add clip and image to group
group.appendChild(clip);
group.appendChild(image);
}
group.setAttributeNS(null, "display", "inline");
}
function updatePath()
{
var pathId = "path" + this.id;
path = svgDocument.getElementById(pathId);
if(path != null)
path.setAttributeNS(null,"d", this.region.getPathStr());
}
function updateImage(cx, cy, rotateFlag, rx, ry)
{
var groupId = "group" + this.id;
group = svgDocument.getElementById(groupId);
if(group == null)
return;
var sin, cos;
if(rotateFlag == 1) //90
{
sin = 1;
cos = 0;
}
else if(rotateFlag == 2) //180
{
sin = 0;
cos = -1;
}
else if(rotateFlag == 3) //270
{
sin = -1;
cos = 0;
}
else //default 0
{
sin = 0;
cos = 1;
}
//Update tranform matrix
var transMatrix = group.getCTM();
var newMatrix = svgRoot.createSVGMatrix();
newMatrix.a = transMatrix.a * cos - transMatrix.b * sin;
newMatrix.b = transMatrix.a * sin + transMatrix.b * cos;
newMatrix.c = transMatrix.c * cos - transMatrix.d * sin;
newMatrix.d = transMatrix.c * sin + transMatrix.d * cos;
newMatrix.e = transMatrix.e * cos - transMatrix.f * sin - rx * cos + ry * sin + rx + cx ;
newMatrix.f = transMatrix.e * sin + transMatrix.f * cos - rx * sin - ry * cos + ry + cy ;
var transformStr = matrixToStr(newMatrix);
group.setAttributeNS(null, "transform", transformStr);
group.setAttributeNS(null, "display", "inline");
//Update image object angle value
this.angle = (this.angle + rotateFlag * 90) % 360;
}
function enableEvent(flag)
{
//Enable or disable mouse event
var elem = svgDocument.getElementById("image" + this.id);
if(elem == null)
return;
if(flag)
elem.setAttributeNS(null, "pointer-events", "all");
else
elem.setAttributeNS(null, "pointer-events", "none");
}
function checkNeighbour(newImage)
{
if(newImage == null)
return;
//Same direction
if(this.angle != newImage.angle)
{
//alert("direction wrong");
return false;
}
//Check distance scope
var group1Id = "group" + this.id;
var group1 = svgDocument.getElementById(group1Id);
var transMatrix1 = group1.getCTM();
var group2Id = "group" + newImage.id;
var group2 = svgDocument.getElementById(group2Id);
var transMatrix2 = group2.getCTM();
var cx = transMatrix1.e - transMatrix2.e;
var cy = transMatrix1.f - transMatrix2.f;
if( (cx*cx + cy*cy) > (MIN_MERGE_DISTANCE * MIN_MERGE_DISTANCE) )
{
//alert("distance wrong");
return false;
}
//Have neighbour block
if( this.region.isNeighbour(newImage.region) == false )
{
//alert("blocks wrong");
return false;
}
//Really a neighbour
return true;
}
///
/// ImageObject class finished
///
//
//Mouse event handlers
//
//Grab by mouse
function grab(evt)
{
if(evt.button != 0) //Left mouse button
return;
var targetElement = evt.target;
if ( background == targetElement )
{
//Do nothing about background
return;
}
var id = targetElement.getAttributeNS(null, "id");
//alert(id);
dragImage = getImageObject(id);
if(dragImage == null)
{
//alert("No drag");
return;
}
//Swap svg node by id, move target to front(appendChild prove to be very slowly)
var canvas = svgDocument.getElementById(CANVAS_ID);
var lastId = canvas.lastChild.getAttributeNS(null, "id");
if( id.substr(5) == lastId.substr(5))
{
swapFlag = false; // Front one, do not need swap
}
else
{
//var lastImage = getImageObject(lastId);
swapImage(id.substr(5), lastId.substr(5));
swapFlag = true;
}
//Set selected flag
//canvas.lastChild.setAttributeNS(null, "opacity", "1");
//Remember old position
orgPoint.x = trueCoords.x;
orgPoint.y = trueCoords.y;
grabPoint.x = trueCoords.x;
grabPoint.y = trueCoords.y;
dragImage.enableEvent(false);
showOutline(dragImage.region.getOutline());
}
function drag(evt)
{
getTrueCoords(evt);
if (dragImage)
{
var newX = trueCoords.x - grabPoint.x;
var newY = trueCoords.y - grabPoint.y;
dragImage.updateImage(newX, newY, 0, 0, 0);
grabPoint.x = trueCoords.x;
grabPoint.y = trueCoords.y;
}
}
function drop(evt)
{
getTrueCoords(evt);
var mergeFlag = false;
if ( dragImage )
{
var newImage = getNeighbourImage();
if(newImage != null)
{
//Merge happened
dragImage.mergeImage(newImage);
dragImage.updatePath();
showOutline(dragImage.region.getOutline());
//Finish or not
if(dragImage.region.blocks.length == imageRow * imageCol)
{
//Congratulations!
setTimeout("gameOver()", 500);
}
mergeFlag = true;
newImage.deleteFromCanvas(); //Delete SVG node
newImage = null;
}
if( (!swapFlag) && (!mergeFlag) )
{
//Rotate image in case of no swap and no merge
var cx = trueCoords.x - orgPoint.x;
var cy = trueCoords.y - orgPoint.y;
if( (cx*cx + cy*cy) <= (MIN_ROTATE_DISTANCE * MIN_ROTATE_DISTANCE) )
{
var newX = trueCoords.x - grabPoint.x;
var newY = trueCoords.y - grabPoint.y;
dragImage.updateImage(newX, newY, 1, trueCoords.x, trueCoords.y);
}
}
dragImage.enableEvent(true);
dragImage = null;
swapFlag = false;
}
}
// ]]>
</script>
<defs>
<menu id="menu1" >
<header>Menu</header>
</menu>
</defs>
<rect id="background" x="0" y="0" width="100%" height="100%" fill="white" pointer-events="all" />
<g id="canvas" >
</g>
</svg>