国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Canvas 進(jìn)階(三)ts + canvas 重寫”辨色“小游戲

Aceyclee / 2493人閱讀

摘要:話不多說,先上和項(xiàng)目源碼有趣的是,在我寫完這篇文章之后,發(fā)現(xiàn)愛編程的李先森也寫了一篇手寫辨色力小游戲?qū)崿F(xiàn)方式有所不同,可以對比下。

1. 背景

之前寫過一篇文章 ES6 手寫一個(gè)“辨色”小游戲, 感覺好玩挺不錯(cuò)。豈料評(píng)論區(qū)大神頻出,其中有人指出,打開控制臺(tái),輸入以下代碼:

setInterval( ()=>document.querySelector("#special-block").click(),1)

即可破解,分?jǐn)?shù)蹭蹭上漲,這不就是bug嗎?同時(shí)評(píng)論區(qū) 【愛編程的李先森】建議,讓我用 canvas 來畫會(huì)更簡單,因此有了這篇文章。

話不多說,先上 Demo 和 項(xiàng)目源碼

有趣的是,在我寫完這篇文章之后,發(fā)現(xiàn)【愛編程的李先森】也寫了一篇canvas手寫辨色力小游戲,實(shí)現(xiàn)方式有所不同,可以對比下。

2. 實(shí)現(xiàn)

本項(xiàng)目基于 typescriptcanvas 實(shí)現(xiàn)

(1) 首先定義配置項(xiàng)

一個(gè)canvas標(biāo)簽,游戲總時(shí)長time, 開始函數(shù)start, 結(jié)束函數(shù)end

interface BaseOptions {
  time?: number;
  end?: Function;
  start?: Function;
  canvas?: HTMLCanvasElement;
}

定義類 ColorGame 實(shí)現(xiàn)的接口 ColorGameType, init()初始化方法,nextStep()下一步,reStart()重新開始方法

interface ColorGameType {
  init: Function;
  nextStep: Function;
  reStart: Function;
}

定義一個(gè)坐標(biāo)對象,用于儲(chǔ)存每個(gè)色塊的起始點(diǎn)

interface Coordinate {
  x: number;
  y: number;
}
(2) 實(shí)現(xiàn)類 ColorGame

定義好了需要用到的接口,再用類去實(shí)現(xiàn)它

class ColorGame implements ColorGameType {
  option: BaseOptions;
  step: number; // 步
  score: number; // 得分
  time: number; // 游戲總時(shí)間
  blockWidth: number; // 盒子寬度
  randomBlock: number; // 隨機(jī)盒子索引
  positionArray: Array; // 存放色塊的數(shù)組
  constructor(userOption: BaseOptions) {
    // 默認(rèn)設(shè)置
    this.option = {
      time: 30, // 總時(shí)長
      canvas: document.getElementById("canvas"),
      start: () => {
        document.getElementById("result").innerHTML = "";
        document.getElementById("screen").style.display = "block";
      },
      end: (score: number) => {
        document.getElementById("screen").style.display = "none";
        document.getElementById(
          "result"
        ).innerHTML = `
您的得分是: ${score}
點(diǎn)擊重新玩一次
`; // @ts-ignore addEvent(document.getElementById("restart"), "click", () => { this.reStart(); }); } // 結(jié)束函數(shù) }; this.init(userOption); // 初始化,合并用戶配置 } init(userOption: BaseOptions) { } nextStep() {} // 重新開始其實(shí)也是重新init()一次 reStart() { this.init(this.option); } }
(3)實(shí)現(xiàn) init() 方法

init() 方法實(shí)現(xiàn)參數(shù)初始化,執(zhí)行 start() 方法,并在最后執(zhí)行 nextStep() 方法,并監(jiān)聽 canvasmousedowntouchstart 事件。

這里用到 canvas.getContext("2d").isPointInPath(x, y) 判斷點(diǎn)擊點(diǎn)是否處于最后一次繪畫的矩形內(nèi),因此特殊顏色的色塊要放在最后一次繪制

init(userOption: BaseOptions) {
    if (this.option.start) this.option.start();
    this.step = 0; // 步驟初始化
    this.score = 0;// 分?jǐn)?shù)初始化
    this.time = this.option.time; // 倒計(jì)時(shí)初始化
    // 合并參數(shù)
    if (userOption) {
      if (Object.assign) {
        Object.assign(this.option, userOption);
      } else {
        extend(this.option, userOption, true);
      }
    }
    
    // 設(shè)置初始時(shí)間和分?jǐn)?shù)
    document.getElementsByClassName(
      "wgt-score"
    )[0].innerHTML = `得分:${this.score}
    時(shí)間:${this.time}`;

    // 開始計(jì)時(shí)
    (window).timer = setInterval(() => {
      if (this.time === 0) {
        clearInterval((window).timer);
        this.option.end(this.score);
      } else {
        this.time--;
        document.getElementById("timer").innerHTML = this.time.toString();
      }
    }, 1000);
    
    this.nextStep(); // 下一關(guān)
    ["mousedown", "touchstart"].forEach(event => {
      this.option.canvas.addEventListener(event, e => {
        let loc = windowToCanvas(this.option.canvas, e);
        // isPointInPath 判斷是否在最后一次繪制矩形內(nèi)
        if (this.option.canvas.getContext("2d").isPointInPath (loc.x, loc.y)) {
          this.nextStep();
          this.score++;
          document.getElementById("score").innerHTML = this.score.toString();
        }
      });
    });
  }
(4)實(shí)現(xiàn) nextStep() 方法

nexStep() 這里實(shí)現(xiàn)的是每一回合分?jǐn)?shù)增加,以及畫面的重新繪畫,這里我用了 this.blockWidth 存放每一級(jí)色塊的寬度, this.randomBlock 存放隨機(jī)特殊顏色色塊的index, this.positionArray 用于存放每個(gè)色塊的左上角坐標(biāo)點(diǎn),默認(rèn)設(shè)置色塊之間為2像素的空白間距。

有一個(gè)特殊的地方是在清除畫布時(shí)ctx.clearRect(0, 0, canvas.width, canvas.width);,需要先 ctx.beginPath();清除之前記憶的路徑。否則會(huì)出現(xiàn)以下的效果:

nextStep() {
    // 記級(jí)
    this.step++;
    let col: number; // 列數(shù)
    if (this.step < 6) {
      col = this.step + 1;
    } else if (this.step < 12) {
      col = Math.floor(this.step / 2) * 2;
    } else if (this.step < 18) {
      col = Math.floor(this.step / 3) * 3;
    } else {
      col = 16;
    }
    let canvas = this.option.canvas;
    let ctx = canvas.getContext("2d");
    ctx.beginPath();
    ctx.clearRect(0, 0, canvas.width, canvas.width); // 清除畫布
    ctx.closePath();
    // 小盒子寬度
    this.blockWidth = (canvas.width - (col - 1) * 2) / col;
    // 隨機(jī)盒子index
    this.randomBlock = Math.floor(col * col * Math.random());
    // 解構(gòu)賦值獲取一般顏色和特殊顏色
    let [normalColor, specialColor] = getColor(this.step);

    this.positionArray = [];
    for (let i = 0; i < col ** 2; i++) {
      let row = Math.floor(i / col);
      let colNow = i % col;
      let x = colNow * (this.blockWidth + 2),
        y = row * (this.blockWidth + 2);

      this.positionArray.push({
        x,
        y
      });
      if (i !== this.randomBlock)
        drawItem(ctx, normalColor, x, y, this.blockWidth, this.blockWidth);
    }

    ctx.beginPath();
    drawItem(
      ctx,
      specialColor,
      this.positionArray[this.randomBlock].x,
      this.positionArray[this.randomBlock].y,
      this.blockWidth,
      this.blockWidth
    );
    ctx.closePath();
  }

drawItem()用于繪制每一個(gè)色塊, 這里需要指出的是,isPointInPath 是判斷是否處于矩形的路徑上,只有使用 context.fill() 才能使整個(gè)矩形成為判斷的路徑。

function drawItem(
  context: Context,
  color: string,
  x: number,
  y: number,
  width: number,
  height: number
): void {
  context.fillStyle = `#${color}`;
  context.rect(x, y, width, height);
  context.fill(); //替代fillRect();
}
(5) 其他共用方法 gameMethods.tsutils.ts
// gameMethods.ts
/**
 * 根據(jù)關(guān)卡等級(jí)返回相應(yīng)的一般顏色和特殊顏色
 * @param {number} step 關(guān)卡
 */
export function getColor(step: number): Array {
  let random = Math.floor(100 / step);
  let color = randomColor(17, 255),
    m: Array = color.match(/[da-z]{2}/g);
  for (let i = 0; i < m.length; i++) m[i] = parseInt(String(m[i]), 16); //rgb
  let specialColor =
    getRandomColorNumber(m[0], random) +
    getRandomColorNumber(m[1], random) +
    getRandomColorNumber(m[2], random);
  return [color, specialColor];
}
/**
 * 返回隨機(jī)顏色的一部分值
 * @param num 數(shù)字
 * @param random 隨機(jī)數(shù)
 */
export function getRandomColorNumber(
  num: number | string,
  random: number
): string {
  let temp = Math.floor(Number(num) + (Math.random() < 0.5 ? -1 : 1) * random);
  if (temp > 255) {
    return "ff";
  } else if (temp > 16) {
    return temp.toString(16);
  } else if (temp > 0) {
    return "0" + temp.toString(16);
  } else {
    return "00";
  }
}
// 隨機(jī)顏色 min 大于16
export function randomColor(min: number, max: number): string {
  var r = randomNum(min, max).toString(16);
  var g = randomNum(min, max).toString(16);
  var b = randomNum(min, max).toString(16);
  return r + g + b;
}
// 隨機(jī)數(shù)
export function randomNum(min: number, max: number): number {
  return Math.floor(Math.random() * (max - min) + min);
}
// utils.ts
/**
 * 合并兩個(gè)對象
 * @param o 默認(rèn)對象
 * @param n 自定義對象
 * @param override 是否覆蓋默認(rèn)對象
 */
export function extend(o: any, n: any, override: boolean): void {
  for (var p in n) {
    if (n.hasOwnProperty(p) && (!o.hasOwnProperty(p) || override)) o[p] = n[p];
  }
}

/**
 *   事件兼容方法
 * @param element dom元素
 * @param type 事件類型
 * @param handler 事件處理函數(shù)
 */
export function addEvent(element: HTMLElement, type: string, handler: any) {
  if (element.addEventListener) {
    element.addEventListener(type, handler, false);
    // @ts-ignore
  } else if (element.attachEvent) {
    // @ts-ignore
    element.attachEvent("on" + type, handler);
  } else {
    // @ts-ignore
    element["on" + type] = handler;
  }
}

/**
 * 獲取點(diǎn)擊點(diǎn)于canvas內(nèi)的坐標(biāo)
 * @param canvas canvas對象
 * @param e 點(diǎn)擊事件
 */
export function windowToCanvas(canvas: HTMLCanvasElement, e: any) {
  let bbox = canvas.getBoundingClientRect(),
    x = IsPC() ? e.clientX || e.clientX : e.changedTouches[0].clientX,
    y = IsPC() ? e.clientY || e.clientY : e.changedTouches[0].clientY;

  return {
    x: x - bbox.left,
    y: y - bbox.top
  };
}

/**
 * 判斷是否為 PC 端,若是則返回 true,否則返回 flase
 */
export function IsPC() {
  let userAgentInfo = navigator.userAgent,
    flag = true,
    Agents = [
      "Android",
      "iPhone",
      "SymbianOS",
      "Windows Phone",
      "iPad",
      "iPod"
    ];

  for (let v = 0; v < Agents.length; v++) {
    if (userAgentInfo.indexOf(Agents[v]) > 0) {
      flag = false;
      break;
    }
  }
  return flag;
}
3. 使用

將代碼打包構(gòu)建后引入 html 后,新建 new ColorGame(option) 即可實(shí)現(xiàn)。前提是頁面結(jié)構(gòu)如下:



  
    
    
    
    canvas 辨色小游戲
    
  
  
    

辨色力測試

找出所有色塊里顏色不同的一個(gè)

開始挑戰(zhàn)

辨色力測試

總結(jié)

這里主要是對 isPointInPath 的使用實(shí)踐,在之后的文章《canvas繪制九宮格》也會(huì)用到此方法,敬請期待!

更多推薦

前端進(jìn)階小書(advanced_front_end)

前端每日一題(daily-question)

webpack4 搭建 Vue 應(yīng)用(createVue)

Canvas 進(jìn)階(一)二維碼的生成與掃碼識(shí)別

Canvas 進(jìn)階(二)寫一個(gè)生成帶logo的二維碼npm插件

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/105439.html

相關(guān)文章

  • Canvas 進(jìn)階(二)寫一個(gè)生成帶logo的二維碼npm插件

    摘要:背景最近接觸到的需求,前端生成一個(gè)帶企業(yè)的二維碼,并支持點(diǎn)擊下載它。 背景 最近接觸到的需求,前端生成一個(gè)帶企業(yè)logo的二維碼,并支持點(diǎn)擊下載它。 實(shí)現(xiàn) 在前面的文章有講到如何用 canvas 畫二維碼,在此基礎(chǔ)上再畫一個(gè)公司logo,并提供下載的方法供調(diào)用,再封裝成 npm 插件 模塊名稱: qrcode-with-logos github地址:https://github.com...

    Chaz 評(píng)論0 收藏0
  • 使用TypeScript和Canvas編寫移動(dòng)端貪吃蛇大作戰(zhàn)游戲

    摘要:基本介紹一款移動(dòng)端貪吃蛇大作戰(zhàn)游戲。主要的游戲邏輯有貪吃蛇移動(dòng)碰撞檢測貪吃蛇碰撞碰撞墻壁和吃食物。貪吃蛇的身體如何跟隨頭部移動(dòng)需要分為兩種情況,在單位時(shí)間內(nèi)貪吃蛇移動(dòng)一單位長度和貪吃蛇移動(dòng)多單位長度。 基本介紹 一款移動(dòng)端貪吃蛇大作戰(zhàn)游戲。(只支持移動(dòng)端) 這是一個(gè)臨近 deadline 的課設(shè)項(xiàng)目,為了方便地使用TS,我直接使用angular-cli生成了TypeScript的項(xiàng)...

    AlphaWallet 評(píng)論0 收藏0
  • 使用Laya引擎開發(fā)微信游戲(上)

    摘要:本文由云社區(qū)發(fā)表使用一個(gè)簡單的游戲開發(fā)示例,由淺入深,介紹了如何用引擎開發(fā)微信小游戲。前段時(shí)間正好抽空研究了一下這塊的內(nèi)容,現(xiàn)做一個(gè)總結(jié),針對如何使用引擎開發(fā)微信小游戲給大家做一下介紹。 本文由云+社區(qū)發(fā)表 使用一個(gè)簡單的游戲開發(fā)示例,由淺入深,介紹了如何用Laya引擎開發(fā)微信小游戲。 showImg(https://segmentfault.com/img/remote/146000...

    zhjx922 評(píng)論0 收藏0
  • ES6 手寫一個(gè)“辨色游戲

    摘要:前言依稀記得幾年前朋友圈流行的辨色小游戲,找出顏色不同的矩形。前些天突發(fā)奇想,打算自己手寫一個(gè)類似的游戲,話不多說,先上。顏色由三色構(gòu)成,三色值越接近,則顏色顯示越接近。 showImg(https://segmentfault.com/img/bVbhaOC?w=1003&h=474); 1. 前言 依稀記得幾年前朋友圈流行的辨色小游戲,找出顏色不同的矩形。前些天突發(fā)奇想,打算自己手...

    feng409 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<