拼图游戏

为了测试IE+ASV3.03, FF1.5和OPERA9.02的图形处理速度,编写了一个拼图的小游戏。

有几个代码说明如下:
1,对象模型:


2,定点旋转后移动的坐标转换公式以及代码


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" οnlοad="init(evt)" οnmοusedοwn="grab(evt)" οnmοusemοve="drag(evt)" οnmοuseup="drop(evt)">
<title>Puzzle</title>
<desc>Puzzle</desc>

<script language="JavaScript" >
<![CDATA[
/*
2006-10-21
Wang Zhao Yan
EMail: wang_zhaoyan2000@yahoo.com.cn
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>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值