canvas图片生成各种拼图裁切分解代码

代码语言:html

所属分类:其他

代码描述:canvas图片生成各种拼图裁切分解代码

代码标签: canvas 图片 生成 各种 拼图 裁切 分解 代码

下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开

<!DOCTYPE html>
<html lang="en" >

<head>
  <meta charset="UTF-8">
  

  
  
  
<style>
body {
      font-family: Arial, Helvetica, "Liberation Sans", FreeSans, sans-serif;
      background-color: #fff;
      margin: 0;
      padding: 0;
      border-width: 0;
      cursor: pointer;
    }

    #menu {
      position: relative;
      list-style-type: none;
      padding-left: 5px;
      z-index: 1000;
      /* 1 */
      display: inline-block;
      text-align: center;
    }

    #menu li {
      margin: 2px;
      padding: 4px 10px;
      border-radius: 5px;
      background-color: #ffff80;
    }

    #menu li:hover {
      background-color: #ffDD60;
    }

    #forPuzzle {
      position: absolute;
      width: 95vw;
      height: 95vh;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      background-color: #ffffdd;
      overflow: hidden;
    }

    .polypiece {
      display: block;
      overflow: hidden;
      position: absolute;
    }

    .moving {
      transition-property: top, left;
      transition-duration: 1s;
      transition-timing-function: linear;
    }

    .gameCanvas {
      display: none;
      overflow: hidden;
      position: absolute;
    }
</style>


  
  
</head>

<body translate="no">
  <div id=forPuzzle></div>
  <ul id="menu">
    <li>&#x2630;</li>
    <li>Reset</li>
    <li>load image</li>
    <li>shape: <select id="shape">
        <option value="1" selected>classic</option>
        <option value="2">triangle</option>
        <option value="3">round</option>
        <option value="4">straight</option>
      </select></li>
    <li>12 pieces</li>
    <li>25 pieces</li>
    <li>50 pieces</li>
    <li>100 pieces</li>
    <li>200 pieces</li>
  </ul>
  
      <script >
"use strict";

let puzzle, autoStart;

const mhypot = Math.hypot,
mrandom = Math.random,
mmax = Math.max,
mmin = Math.min,
mround = Math.round,
mfloor = Math.floor,
msqrt = Math.sqrt,
mabs = Math.abs;
//-----------------------------------------------------------------------------
function isMiniature() {
  return location.pathname.includes('/fullcpgrid/');
}
//-----------------------------------------------------------------------------

function alea(min, max) {
  // random number [min..max[ . If no max is provided, [0..min[

  if (typeof max == 'undefined') return min * mrandom();
  return min + (max - min) * mrandom();
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

function intAlea(min, max) {
  // random integer number [min..max[ . If no max is provided, [0..min[

  if (typeof max == 'undefined') {
    max = min;min = 0;
  }
  return mfloor(min + (max - min) * mrandom());
} // intAlea

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

function arrayShuffle(array) {
  /* randomly changes the order of items in an array
  only the order is modified, not the elements
  */
  let k1, temp;
  for (let k = array.length - 1; k >= 1; --k) {
    k1 = intAlea(0, k + 1);
    temp = array[k];
    array[k] = array[k1];
    array[k1] = temp;
  } // for k
  return array;
} // arrayShuffle

//-----------------------------------------------------------------------------

// Point - - - - - - - - - - - - - - - - - - - -
class Point {
  constructor(x, y) {
    this.x = Number(x);
    this.y = Number(y);
  } // constructor
  copy() {
    return new Point(this.x, this.y);
  }

  distance(otherPoint) {
    return mhypot(this.x - otherPoint.x, this.y - otherPoint.y);
  }}
// class Point

// Segment - - - - - - - - - - - - - - - - - - - -
// those segments are oriented
class Segment {
  constructor(p1, p2) {
    this.p1 = new Point(p1.x, p1.y);
    this.p2 = new Point(p2.x, p2.y);
  }
  dx() {
    return this.p2.x - this.p1.x;
  }
  dy() {
    return this.p2.y - this.p1.y;
  }
  length() {
    return mhypot(this.dx(), this.dy());
  }

  // returns a point at a given distance of p1, positive direction beeing towards p2

  pointOnRelative(coeff) {
    // attention if segment length can be 0
    let dx = this.dx();
    let dy = this.dy();
    return new Point(this.p1.x + coeff * dx, this.p1.y + coeff * dy);
  }}
// class Segment
//-----------------------------------------------------------------------------
// one side of a piece
class Side {
  constructor() {
    this.type = ""; // "d" pour straight line or "z" pour classic
    this.points = []; // real points or Bezier curve points
    // this.scaledPoints will be added when we know the scale
  } // Side

  //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  reversed() {
    // returns a new Side, copy of current one but reversed
    const ns = new Side();
    ns.type = this.type;
    ns.points = this.points.slice().reverse();
    return ns;
  } // Side.reversed

  //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  scale(puzzle) {
    /* uses actual dimensions of puzzle to compute actual side points
    these points are not shifted by the piece position : the top left corner is at (0,0)
    */
    const coefx = puzzle.scalex;
    const coefy = puzzle.scaley;
    this.scaledPoints = this.points.map(p => new Point(p.x * coefx, p.y * coefy));

  } //

  //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  /*
  draws the path corresponding to a side
  Parameters :
    ctx : canvas context
    shiftx, shifty : position shift (used to create emboss effect)
    withoutMoveTo : to decide whether to do a moveTo to the first point. Without MoveTo
    must be done only for the first side of a piece, not for the following ones
  */

  drawPath(ctx, shiftx, shifty, withoutMoveTo) {

    if (!withoutMoveTo) {
      ctx.moveTo(this.scaledPoints[0].x + shiftx, this.scaledPoints[0].y + shifty);
    }
    if (this.type == "d") {
      ctx.lineTo(this.scaledPoints[1].x + shiftx, this.scaledPoints[1].y + shifty);
    } else {// edge zigzag
      for (let k = 1; k < this.scaledPoints.length - 1; k += 3) {
        ctx.bezierCurveTo(this.scaledPoints[k].x + shiftx, this.scaledPoints[k].y + shifty,
        this.scaledPoints[k + 1].x + shiftx, this.scaledPoints[k + 1].y + shifty,
        this.scaledPoints[k + 2].x + shiftx, this.scaledPoints[k + 2].y + shifty);
      } // for k
    } // if jigsaw side

  } // Side.drawPath
} // class Side

//-----------------------------------------------------------------------------
/* modifies a side
  changes it from a straight line (type "d") to a complex one (type "z")
  The change is done towards the opposite side (side between corners ca and cb)
*/

function twist0(side, ca, cb) {

  const seg0 = new Segment(side.points[0], side.points[1]);
  const dxh = seg0.dx();
  const dyh = seg0.dy();

  const seg1 = new Segment(ca, cb);
  const mid0 = seg0.pointOnRelative(0.5);
  const mid1 = seg1.pointOnRelative(0.5);

  const segMid = new Segment(mid0, mid1);
  const dxv = segMid.dx();
  const dyv = segMid.dy();

  const scalex = alea(0.8, 1);
  const scaley = alea(0.9, 1);
  const mid = alea(0.45, 0.55);

  const pa = pointAt(mid - 1 / 12 * scalex, 1 / 12 * scaley);
  const pb = pointAt(mid - 2 / 12 * scalex, 3 / 12 * scaley);
  const pc = pointAt(mid, 4 / 12 * scaley);
  const pd = pointAt(mid + 2 / 12 * scalex, 3 / 12 * scaley);
  const pe = pointAt(mid + 1 / 12 * scalex, 1 / 12 * scaley);

  side.points = [seg0.p1,
  new Point(seg0.p1.x + 5 / 12 * dxh * 0.52,
  seg0.p1.y + 5 / 12 * dyh * 0.52),
  new Point(pa.x - 1 / 12 * dxv * 0.72,
  pa.y - 1 / 12 * dyv * 0.72),
  pa,
  new Point(pa.x + 1 / 12 * dxv * 0.72,
  pa.y + 1 / 12 * dyv * 0.72),

  new Point(pb.x - 1 / 12 * dxv * 0.92,
  pb.y - 1 / 12 * dyv * 0.92),
  pb,
  new Point(pb.x + 1 / 12 * dxv * 0.52,
  pb.y + 1 / 12 * dyv * 0.52),
  new Point(pc.x - 2 / 12 * dxh * 0.40,
  pc.y - 2 / 12 * dyh * 0.40),
  pc,
  new Point(pc.x + 2 / 12 * dxh * 0.40,
  pc.y + 2 / 12 * dyh * 0.40),
  new Point(pd.x + 1 / 12 * dxv * 0.52,
  pd.y + 1 / 12 * dyv * 0.52),
  pd,
  new Point(pd.x - 1 / 12 * dxv * 0.92,
  pd.y - 1 / 12 * dyv * 0.92),
  new Point(pe.x + 1 / 12 * dxv * 0.72,
  pe.y + 1 / 12 * dyv * 0.72),
  pe,
  new Point(pe.x - 1 / 12 * dxv * 0.72,
  pe.y - 1 / 12 * dyv * 0.72),
  new Point(seg0.p2.x - 5 / 12 * dxh * 0.52,
  seg0.p2.y - 5 / 12 * dyh * 0.52),
  seg0.p2];
  side.type = "z";

  function pointAt(coeffh, coeffv) {
    return new Point(seg0.p1.x + coeffh * dxh + coeffv * dxv,
    seg0.p1.y + coeffh * dyh + coeffv * dyv);
  } // pointAt

} // twist0

//-----------------------------------------------------------------------------
/* modifies a side
  changes it from a straight line (type "d") to a complex one (type "z")
  The change is done towards the opposite side (side between corners ca and cb)
*/

function twist1(side, ca, cb) {

  const seg0 = new Segment(side.points[0], side.points[1]);
  const dxh = seg0.dx();
  const dyh = seg0.dy();

  const seg1 = new Segment(ca, cb);
  const mid0 = seg0.pointOnRelative(0.5);
  const mid1 = seg1.pointOnRelative(0.5);

  const segMid = new Segment(mid0, mid1);
  const dxv = segMid.dx();
  const dyv = segMid.dy();

  const pa = pointAt(alea(0.3, 0.35), alea(-0.05, 0.05));
  const pb = pointAt(alea(0.45, 0.55), alea(0.2, 0.3));
  const pc = pointAt(alea(0.65, 0.78), alea(-0.05, 0.05));

  side.points = [seg0.p1,
  seg0.p1, pa, pa,
  pa, pb, pb,
  pb, pc, pc,
  pc, seg0.p2, seg0.p2];
  side.type = "z";

  function pointAt(coeffh, coeffv) {
    return new Point(seg0.p1.x + coeffh * dxh + coeffv * dxv,
    seg0.p1.y + coeffh * dyh + coeffv * dyv);
  } // pointAt

} // twist1

//-----------------------------------------------------------------------------
/* modifies a side
  changes it from a straight line (type "d") to a complex one (type "z")
  The change is done towards the opposite side (side between corners ca and cb)
*/

function twist2(side, ca, cb) {

  const seg0 = new Segment(side.points[0], side.points[1]);
  const dxh = seg0.dx();
  const dyh = seg0.dy();

  const seg1 = new Segment(ca, cb);
  const mid0 = seg0.pointOnRelative(0.5);
  const mid1 = seg1.pointOnRelative(0.5);

  const segMid = new Segment(mid0, mid1);
  const dxv = segMid.dx();
  const dyv = segMid.dy();

  const hmid = alea(0.45, 0.55);
  const vmid = alea(0.4, 0.5);
  const pc = pointAt(hmid, vmid);
  let sega = new Segment(seg0.p1, pc);

  const pb = sega.pointOnRelative(2 / 3);
  sega = new Segment(seg0.p2, pc);
  const pd = sega.pointOnRelative(2 / 3);

  side.points = [seg0.p1, pb, pd, seg0.p2];
  side.type = "z";

  function pointAt(coeffh, coeffv) {
    return new Point(seg0.p1.x + coeffh * dxh + coeffv * dxv,
    seg0.p1.y + coeffh * dyh + coeffv * dyv);
  } // pointAt

} // twist2

//-----------------------------------------------------------------------------
/* modifies a side
  changes it from a straight line (type "d") to a complex one (type "z")
  The change is done towards the opposite side (side between corners ca and cb)
*/

function twist3(side, ca, cb) {

  side.points = [side.points[0], side.points[1]];

} // twist3


//-----------------------------------------------------------------------------
class Piece {
  constructor(kx, ky) {// object with 4 sides
    this.ts = new Side(); // top side
    this.rs = new Side(); // right side
    this.bs = new Side(); // bottom side
    this.ls = new Side(); // left side
    this.kx = kx;
    this.ky = ky;
  }

  //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  scale(puzzle) {
    this.ts.scale(puzzle);
    this.rs.scale(puzzle);
    this.bs.scale(puzzle);
    this.ls.scale(puzzle);
  } // Piece.scale
} // class Piece
//--------------------------------------------------------------
//--------------------------------------------------------------
class PolyPiece {

  // represents a group of pieces well positionned with respect  to each other.
  // pckxmin, pckxmax, pckymin and pckymax record the lowest and highest kx and ky
  // creates a canvas to draw polypiece on, and appends this canvas to puzzle.container
  constructor(initialPiece, puzzle) {
    this.pckxmin = initialPiece.kx;
    this.pckxmax = initialPiece.kx + 1;
    this.pckymin = initialPiece.ky;
    this.pckymax = initialPiece.ky + 1;
    this.pieces = [initialPiece];
    this.puzzle = puzzle;
    this.listLoops();

    this.canvas = document.createElement('CANVAS');
    // size and z-index will be defined later
    puzzle.container.appendChild(this.canvas);
    this.canvas.classList.add('polypiece');
    this.ctx = this.canvas.getContext("2d");
  } // PolyPiece

  // -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -   -
  /*
    this method
      - adds pieces of otherPoly to this PolyPiece
      - reorders the pieces inside the polypiece
      - adjusts coordinates of new pieces to make them consistent with this polyPiece
      - re-evaluates the z - index of the polyPieces
  */

  merge(otherPoly) {

    const orgpckxmin = this.pckxmin;
    const orgpckymin = this.pckymin;

    // remove otherPoly from list of polypieces
    const kOther = this.puzzle.polyPieces.indexOf(otherPoly);
    this.puzzle.polyPieces.splice(kOther, 1);

    // remove other canvas from container
    this.puzzle.container.removeChild(otherPoly.canvas);

    for (let k = 0; k < otherPoly.pieces.length; ++k) {
      this.pieces.push(otherPoly.pieces[k]);
      // watch leftmost, topmost... pieces
      if (otherPoly.pieces[k].kx < this.pckxmin) this.pckxmin = otherPoly.pieces[k].kx;
      if (otherPoly.pieces[k].kx + 1 > this.pckxmax) this.pckxmax = otherPoly.pieces[k].kx + 1;
      if (otherPoly.pieces[k].ky < this.pckymin) this.pckymin = otherPoly.pieces[k].ky;
      if (otherPoly.pieces[k].ky + 1 > this.pckymax) this.pckymax = otherPoly.pieces[k].ky + 1;
    } // for k

    // sort the pieces by increasing kx, ky

    this.pieces.sort(function (p1, p2) {
      if (p1.ky < p2.ky) return -1;
      if (p1.ky > p2.ky) return 1;
      if (p1.kx < p2.kx) return -1;
      if (p1.kx > p2.kx) return 1;
      return 0; // should not occur
    });

    // redefine consecutive edges
    this.listLoops();

    this.drawImage();
    this.moveTo(this.x + this.puzzle.scalex * (this.pckxmin - orgpckxmin),
    this.y + this.puzzle.scaley * (this.pckymin - orgpckymin));

    this.puzzle.evaluateZIndex();
  } // merge

  // -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -   -
  ifNear(otherPoly) {

    let p1, p2;
    let puzzle = this.puzzle;

    // coordinates of origin of full picture for this PolyPieces
    let x = this.x - puzzle.scalex * this.pckxmin;
    let y = this.y - puzzle.scaley * this.pckymin;

    let ppx = otherPoly.x - puzzle.scalex * otherPoly.pckxmin;
    let ppy = otherPoly.y - puzzle.scaley * otherPoly.pckymin;
    if (mhypot(x - ppx, y - ppy) >= puzzle.dConnect) return false; // not close enough

    // this and otherPoly are in good relative position, have they a common side ?
    for (let k = this.pieces.length - 1; k >= 0; --k) {
      p1 = this.pieces[k];
      for (let ko = otherPoly.pieces.length - 1; ko >= 0; --ko) {
        p2 = otherPoly.pieces[ko];
        if (p1.kx == p2.kx && mabs(p1.ky - p2.ky) == 1) return true; // true neighbors found
        if (p1.ky == p2.ky && mabs(p1.kx - p2.kx) == 1) return true; // true neighbors found
      } // for k
    } // for k

    // nothing matches

    return false;

  } // ifNear

  // -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -

  /* algorithm to determine the boundary of a PolyPiece
    input : a table of cells, hopefully defining a 'good' PolyPiece, i.e. all connected together
    every cell is given as an object {kx: indice, ky: indice} representing an element of a 2D array.
     returned value : table of Loops, because the boundary may be made of several
  simple loops : there may be a 'hole' in a PolyPiece
  every loop is a list of consecutive edges,
  every edge if an object {kp: index, edge: b} where kp is the index of the cell ine
  the input array, and edge the side (0(top), 1(right), 2(bottom), 3(left))
  every edge contains kx and ky too, normally not used here
   This method does not depend on the fact that pieces have been scaled or not.
  */



  listLoops() {

    // internal : checks if an edge given by kx, ky is common with another cell
    // returns true or false
    const that = this;
    function edgeIsCommon(kx, ky, edge) {
      let k;
      switch (edge) {
        case 0:ky--;break; // top edge
        case 1:kx++;break; // right edge
        case 2:ky++;break; // bottom edge
        case 3:kx--;break; // left edge
      } // switch
      for (k = 0; k < that.pieces.length; k++) {
        if (kx == that.pieces[k].kx && ky == that.pieces[k].ky) return true; // we found the neighbor
      }
      return false; // not a common edge
    } // function edgeIsCommon

    // -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -
    // internal : checks if an edge given by kx, ky is in tbEdges
    // return index in tbEdges, or false

    function edgeIsInTbEdges(kx, ky, edge) {
      let k;
      for (k = 0; k < tbEdges.length; k++) {
        if (kx == tbEdges[k].kx && ky == tbEdges[k].ky && edge == tbEdges[k].edge) return k; // found it
      }
      return false; // not found
    } // function edgeIsInTbEdges

    // -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -

    let tbLoops = []; // for the result
    let tbEdges = []; // set of edges which are not shared by 2 pieces of input
    let k;
    let kEdge; // to count 4 edges
    let lp; // for loop during its creation
    let currEdge; // current edge
    let tries; // tries counter
    let edgeNumber; // number of edge found during research
    let potNext;

    // table of tries

    let tbTries = [
    // if we are on edge 0 (top)
    [
    { dkx: 0, dky: 0, edge: 1 }, // try # 0
    { dkx: 1, dky: 0, edge: 0 }, // try # 1
    { dkx: 1, dky: -1, edge: 3 } // try # 2
    ],
    // if we are on edge 1 (right)
    [
    { dkx: 0, dky: 0, edge: 2 },
    { dkx: 0, dky: 1, edge: 1 },
    { dkx: 1, dky: 1, edge: 0 }],

    // if we are on edge 2 (bottom)
    [
    { dkx: 0, dky: 0, edge: 3 },
    { dkx: -1, dky: 0, edge: 2 },
    { dkx: -1, dky: 1, edge: 1 }],

    // if we are on edge 3 (left)
    [
    { dkx: 0, dky: 0, edge: 0 },
    { dkx: 0, dky: -1, edge: 3 },
    { dkx: -1, dky: -1, edge: 2 }]];



    // create list of not shared edges (=> belong to boundary)
    for (k = 0; k < this.pieces.length; k++) {
      for (kEdge = 0; kEdge < 4; kEdge++) {
        if (!edgeIsCommon(this.pieces[k].kx, this.pieces[k].ky, kEdge))
        tbEdges.push({ kx: this.pieces[k].kx, ky: this.pieces[k].ky, edge: kEdge, kp: k });
      } // for kEdge
    } // for k

    while (tbEdges.length > 0) {
      lp = []; // new loop
      currEdge = tbEdges[0]; // we begin with first available edge
      lp.push(currEdge); // add it to loop
      tbEdges.splice(0, 1); // remove from list of available sides
      do {
        for (tries = 0; tries < 3; tries++) {
          potNext = tbTries[currEdge.edge][tries];
          edgeNumber = edgeIsInTbEdges(currEdge.kx + potNext.dkx, currEdge.ky + potNext.dky, potNext.edge);
          if (edgeNumber === false) continue; // can't here
          // new element in loop
          currEdge = tbEdges[edgeNumber]; // new current edge
          lp.push(currEdge); // add it to loop
          tbEdges.splice(edgeNumber, 1); // remove from list of available sides
          break; // stop tries !
        } // for tries
        if (edgeNumber === false) break; // loop is closed
      } while (1); // do-while exited by break
      tbLoops.push(lp); // add this loop to loops list
    } // while tbEdges...

    // replace components of loops by actual pieces sides
    this.tbLoops = tbLoops.map(loop => loop.map(edge => {
      let cell = this.pieces[edge.kp];
      if (edge.edge == 0) return cell.ts;
      if (edge.edge == 1) return cell.rs;
      if (edge.edge == 2) return cell.bs;
      return cell.ls;
    }));
  } // polyPiece.listLoops

  // -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -   -

  drawPath(ctx, shiftx, shifty) {

    //    ctx.beginPath(); No, not for Path2D

    this.tbLoops.forEach(loop => {
      let without = false;
      loop.forEach(side => {
        side.drawPath(ctx, shiftx, shifty, without);
        without = true;
      });
      ctx.closePath();
    });

  } // PolyPiece.drawPath

  // -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -  -   -

  drawImage() {
    /* resizes canvas to be bigger than if pieces were perfect rectangles
    so that their shapes actually fit in the canvas
    copies the relevant part of gamePicture clipped by path
    adds shadow and emboss
    */
    //       if (this.pieces[0].kx!=1 ||this.pieces[0].ky!= 1) return;
    puzzle = this.puzzle;
    this.nx = this.pckxmax - this.pckxmin + 1;
    this.ny = this.pckymax - this.pckymin + 1;
    this.canvas.width = this.nx * puzzle.scalex;
    this.canvas.height = this.ny * puzzle.scaley;

    // difference between position in this canvas and position in gameImage

    this.offsx = (this.pckxmin - 0.5) * puzzle.scalex;
    this.offsy = (this.pckymin - 0.5) * puzzle.scaley;

    this.path = new Path2D();
    this.drawPath(this.path, -this.offsx, -this.offsy);

    // make shadow
    this.ctx.fillStyle = 'none';
    this.ctx.shadowColor = 'rgba(0, 0, 0, 0.5)';
    this.ctx.shadowBlur = 4;
    this.ctx.shadowOffsetX = 4;
    this.ctx.shadowOffsetY = 4;
    this.ctx.fill(this.path);
    this.ctx.shadowColor = 'rgba(0, 0, 0, 0)'; // stop shadow effect

    this.pieces.forEach((pp, kk) => {

      this.ctx.save();

      const path = new Path2D();
      const shiftx = -this.offsx;
      const shifty = -this.offsy;
      pp.ts.drawPath(path, shiftx, shifty, false);
      pp.rs.drawPath(path, shiftx, shifty, true);
      pp.bs.drawPath(path, shiftx, shifty, true);
      pp.ls.drawPath(path, shiftx, shifty, true);
      path.closePath();

      this.ctx.clip(path);
      // do not copy from negative coordinates, does not work for all browsers
      const srcx = pp.kx ? (pp.kx - 0.5) * puzzle.scalex : 0;
      const srcy = pp.ky ? (pp.ky - 0.5) * puzzle.scaley : 0;

      const destx = (pp.kx ? 0 : puzzle.scalex / 2) + (pp.kx - this.pckxmin) * puzzle.scalex;
      const desty = (pp.ky ? 0 : puzzle.scaley / 2) + (pp.ky - this.pckymin) * puzzle.scaley;

      let w = 2 * puzzle.scalex;
      let h = 2 * puzzle.scaley;
      if (srcx + w > puzzle.gameCanvas.width) w = puzzle.gameCanvas.width - srcx;
      if (srcy + h > puzzle.gameCanvas.height) h = puzzle.gameCanvas.height - srcy;

      this.ctx.drawImage(puzzle.gameCanvas, srcx, srcy, w, h,
      destx, desty, w, h);

      this.ctx.translate(puzzle.embossThickness / 2, -puzzle.embossThickness / 2);
      this.ctx.lineWidth = puzzle.embossThickness;
      this.ctx.strokeStyle = "rgba(0, 0, 0, 0.35)";
      this.ctx.stroke(path);

      this.ctx.translate(-puzzle.embossThickness, puzzle.embossThickness);
      this.ctx.strokeStyle = "rgba(255, 255, 255, 0.35)";
      this.ctx.stroke(path);
      this.ctx.restore();
    });

  } // PolyPiece.drawImage

  moveTo(x, y) {
    // sets the left, top properties (relative to container) of this.canvas
    this.x = x;
    this.y = y;
    this.canvas.style.left = x + 'px';
    this.canvas.style.top = y + 'px';
  } //

  moveToInitialPlace() {
    const puzzle = this.puzzle;
    this.moveTo(puzzle.offsx + (this.pckxmin - 0.5) * puzzle.scalex,
    puzzle.offsy + (this.pckymin - 0.5) * puzzle.scaley);
  }}

// class PolyPiece


//-----------------------------------------------------------------------------
class Puzzle {
  /*
      params contains :
   container : mandatory - given by id (string) or element
              it will not be resized in this script
   ONLY ONE Puzzle object should be instanced.
      only "container is mandatory, nbPieces and pictures may be provided to get
      initial default values.
      Once a puzzle is solved (and event if not solved) another game can be played
      by changing the image file or the number of pieces, NOT by invoking new Puzzle
  */



  constructor(params) {

    this.autoStart = false;

    this.container = typeof params.container == "string" ?
    document.getElementById(params.container) :
    params.container;

    /* the following code will add the event Handlers several times if
      new Puzzle objects are created with same container.
      the presence of previous event listeners is NOT detectable
    */
    this.container.addEventListener("mousedown", event => {
      event.preventDefault();
      events.push({ event: 'touch', position: this.relativeMouseCoordinates(event) });
    });
    this.container.addEventListener("touchstart", event => {
      event.preventDefault();
      if (event.touches.length != 1) return;
      let ev = event.touches[0];
      events.push({ event: 'touch', position: this.relativeMouseCoordinates(ev) });
    }, { passive: false });

    this.container.addEventListener("mouseup", event => {
      event.preventDefault();
      handleLeave();
    });
    this.container.addEventListener("touchend", handleLeave);
    this.container.addEventListener("touchleave", handleLeave);
    this.container.addEventListener("touchcancel", handleLeave);

    this.container.addEventListener("mousemove", event => {
      event.preventDefault();
      // do not accumulate move event.........完整代码请登录后点击上方下载按钮下载查看

网友评论0