1. Introduction

I vaguely remember the popular color discrimination game in moments a few years ago to find rectangles with different colors. A few days ago whim, intends to own handwriting a similar game, words do not say, first on Demo. – project source

This example is based on ES6 implementation and compatible with IE9 and above.

2. Project structure

index.html index.css index.js

This article focuses on how to use JS to achieve functions, HTML CSS is not the scope. Go straight to the code.

<! --index.html-->

      
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <link rel="stylesheet" href="index.css">
  <title>suporka color game</title>
</head>

<body>
  <div class="container">
    <div class="wgt-home" id="page-one">
      <h1>Color discrimination test</h1>
      <p>Find the one of all the color blocks that has a different color</p>
      <a id="start" class="btn btn-primary btn-lg">Began to challenge</a>
    </div>
    <header class="header">
      <h1>Color discrimination test</h1>
    </header>

    <aside class="wgt-score">
    </aside>

    <section id="screen" class="screen">
    </section>
    
    <footer>
      <div> <a href="http://zxpsuper.github.io" style="color: #FAF8EF"> my blog</a></div>
      ©<a href="https://zxpsuper.github.io">Suporka</a>
      ©<a href="https://zxpsuper.github.io/Demo/advanced_front_end/">My book</a>
      ©<a href="https://github.com/zxpsuper">My Github</a>
    </footer>
  </div>
</body>
<! -- <script src="index.js"></script> -->
<script src="colorGame.js"></script>
<script>
  // Event compatible method, compatible with IE
  function addEvent(element, type, handler) {
    if (element.addEventListener) {
      element.addEventListener(type, handler, false);
    } else if (element.attachEvent) {
      element.attachEvent("on" + type, handler);
    } else {
      element["on"+ type] = handler; }}window.onload = function () {
    addEvent(document.querySelector('#start'), 'click'.function() {
      document.querySelector('#page-one').style.display = 'none'
      new ColorGame({
        time: 30})})}</script>
</html>
Copy the code
/*index.css*/
body {
  background-color: #FAF8EF;
}
footer {
  display: block;
  margin-top: 10px;
  text-align: center;
}
h1 {
  font-size: 2em;
  margin:.67em 0;
}
a {
  text-decoration: none;
}
footer a {
  margin-right: 14px;
}
.container {
  margin: auto;
  padding: 0 10px;
  max-width: 600px;
}
.wgt-home {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  padding-top: 50px;
  font-size: 20px;
  background: #fc0;
  text-align: center;
  color: #fff;
}

.wgt-home p {
  margin-top: 4em;
}

.btn {
  display: inline-block;
  margin-bottom: 0;
  font-weight: 400;
  text-align: center;
  vertical-align: middle;
  cursor: auto;
  background-image: none;
  border: 1px solid transparent;
  white-space: nowrap;
  padding: 6px 12px;
  font-size: 14px;
  line-height: 1.42857143;
  border-radius: 4px;
  -webkit-user-select: none;
  user-select: none;
}
.btn-lg {
  padding: 10px 16px;
  font-size: 18px;
  line-height: 1.33;
  border-radius: 6px;
}
.btn-primary {
  color: #fff;
  background-color: #428bca;
  border-color: #357ebd;
}
.wgt-home .btn {
  margin-top: 4em;
  width: 50%;
  max-width: 300px;
}
.screen {
  display: block;
  margin-top: 10px;
  padding: 1px;
}
.screen .block {
  float: left;
  box-sizing: border-box;
  padding: 1px;
}
.screen .block .block-inner {
  content: ' ';
  display: block;
  width: 100%;
  padding-top: 100%;
  border-radius: 2px;
  -webkit-user-select: none;
  user-select: none;
}
.result {
  color: red;
  text-align: center;
  font-size: 20px;
  cursor: pointer;
}
Copy the code
// index.js
// es6 class
class ColorGame {
  constructor() {}}Copy the code

3. Function realization

A gameobject has a default configuration and can be set by the user alone, so —

// index.js
class ColorGame {
  constructor(userOption) {
    this.option = {
      time: 30./ / total duration
      end: score= > {
        document.getElementById(
          "screen"
        ).innerHTML = `<div class="result" style="width: 100%;" > <div class="block-inner" id="restart"> You score is${score} <br/> click to start again</div>
      </div>`;
        addEvent(document.getElementById("restart"), "click", () = > {this.init();
        });
      } // End the function
    }
    this.init(userOption); // Merge user configurations}}Copy the code

What can be configured in this game is the total game time and the end method ().

In the above code, the user’s score is displayed at the end of the game, and the user can click to restart the game. AddEvent () is compatible with IE event monitoring method, the code is as follows:

// Event-compatible methods
function addEvent(element, type, handler) {
  if (element.addEventListener) {
    element.addEventListener(type, handler, false);
  } else if (element.attachEvent) {
    element.attachEvent("on" + type, handler);
  } else {
    element["on"+ type] = handler; }}Copy the code

Init () initializes the game with an argument, and restarts the game without an argument. So –

// index.js
class ColorGame {
  constructor(userOption) {
    // ...
  }
  init(userOption) {

    this.step = 0; / / levels
    this.score = 0; / / score

    if (userOption) {
      if (Object.assign) {
        // Merge user configuration, es6
        Object.assign(this.option, userOption);
      } else {
        // Compatible with ES6 writing
        extend(this.option, userOption, true); }}// Countdown assignment
    this.time = this.option.time;
    // Set the initial time and score
    document.getElementsByClassName(
      "wgt-score") [0].innerHTML = <span id="score">The ${this.score}</span> Time: <span id="timer">The ${this.time}</span>`;

    // Start the timer, es6 arrow function
    window.timer = setInterval((a)= > {
      if (this.time === 0) {
        // If the time is 0, clearInterval calls the end method
        clearInterval(window.timer);
        this.option.end(this.score);
      } else {
        this.time--;
        document.getElementById("timer").innerHTML = this.time; }},1000);

    this.nextStep(); / / the next level}}Copy the code

Where extend() is written for compatibility merge configuration, the specific code is as follows:

// Merge parameter methods
function extend(o, n, override) {
  for (var p in n) {
    if (n.hasOwnProperty(p) && (!o.hasOwnProperty(p) || override))
      o[p] = n[p];
  }
}
Copy the code

NextStep () is the core method for this game, described in more detail below.

// index.js
class ColorGame {
  constructor(userOption) {
    // ...
  }
  init(userOption) {
    // ...
  }
  nextStep() {
  }
}
Copy the code

The main body of the game is n by N matrix graphics, and each small box is the same size, but there is a different color in one of them, and the general color of each level is also different, so we need to randomly pick up a color, and return a special color that gradually approaches the general color as the level increases.

The color is composed of RGB tricolor. The closer the tricolor value is, the closer the color display will be. As the grade increases, the difference between the tricolor values of the two colors is infinitely close to 0. At this time, I remember the inverse proportional function in middle school (infinitely close to the X-axis), and this paper uses 100/step (decreases with the increase of step).

/** * Return normal and special colors according to level * @param {number} step level */
function getColor(step) {
  // RGB random plus or minus random
  let random = Math.floor(100/step);

  // Get random general color, split tri-color value
  let color = randomColor(17.255),
    m = color.match(/[\da-z]{2}/g);

  // Convert to base 10
  for (let i = 0; i < m.length; i++) m[i] = parseInt(m[i], 16); //rgb
  let specialColor =
    getRandomColorNumber(m[0], random) +
    getRandomColorNumber(m[1], random) +
    getRandomColorNumber(m[2], random);
  return [color, specialColor];
}

@param {number} num Monochromatic value @param {number} random random plus or minus value */
function getRandomColorNumber(num, random) {
  let temp = Math.floor(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"; }}/** * Random color * @param {number} min min * @param {number} Max Max */
function randomColor(min, max) {
  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;
}
/** * Random number * @param {number} min min * @param {number} Max Max */
function randomNum(min, max) {
  return Math.floor(Math.random() * (max - min) + min);
}
Copy the code

With the basic methods out of the way, let’s move on to the nextStep() method.

First of all, the matrix must have a maximum number of columns, too small to operate, and the display will not look good.

Secondly, determine the number of columns in each level col, so as to know the total number of small boxes col * col, store the HTML fragment string of each box into the array ARR with the length of Col * col, then randomly change the color of one of them to a special color, and give this div a special ID. Listen for the DOM element’s click event, and if it clicks, proceed to the next level.

// index.js
class ColorGame {
  constructor(userOption) {
    // ...
  }
  init(userOption) {
    // ...
  }
  nextStep() {
    / / record level
    this.step++;
    let col; / / the number of columns
    // Set the number of columns up to 16
    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;
    }

    // Small box width
    let blockWidth = ((100 / col).toFixed(2) * 100 - 1) / 100;

    // random box index
    let randomBlock = Math.floor(col * col * Math.random());

    // Deconstruct assignment to get normal and special colors, es6 deconstruct
    let [normalColor, specialColor] = getColor(this.step);

    // Es6 template character string
    let item = `<div class="block" style="width: ${blockWidth}%;" > <div class="block-inner" style="background-color: #${normalColor}"></div>
  </div>`;

    // An array containing all boxes
    let arr = [];

    // Initialize the array
    for (let i = 0; i < col * col; i++) arr.push(item);

    // Modify random boxes
    arr[randomBlock] = `<div class="block" style="width: ${blockWidth}%;" > <div class="block-inner" style="background-color: #${specialColor}" id="special-block"></div>
  </div>`;

    // Modify the page DOM element
    document.getElementById("screen").innerHTML = arr.join("");

    // Listen for special box click events
    addEvent(document.getElementById("special-block"), "click", () = > {this.nextStep();
      this.score++;
      // Modify the score
      document.getElementById("score").innerHTML = this.score; }); }}Copy the code

At this point, please open index.html, does it do what it should? Is that the end of the story? Well, if you are careful, you may notice that this game won’t work in IE, ie is not compatible with ES6 syntax. How to do?

4. Compatibility and expansion

To be compatible with IE, we need to convert es6 syntax to ES5 and compile using Babel.

We found that this js file can only be imported with the script tag. I want it to be compatible with common. Js or require.js module imports.

–UMD, here is an article about js modularization, which involves UMD, there is a need for students can see — Javascript modularization

Here’s how to use WebPack to implement the above requirements:

// webpack.js

const path = require('path');

module.exports = {
  entry: {
    index: './index.js'./ / the entry
  },
  module: {
    rules: [{test: /\.js$/.exclude: /node_modules/.loader: "babel-loader"}},],plugins: [
    new VueLoaderPlugin(),
  ],
  output: {
    path: path.resolve(__dirname, '/'),
    library: 'ColorGame'.libraryExport: "default".libraryTarget: 'umd'.filename: 'colorGame.js',}};Copy the code

Add export Default ColorGame to the last line of the index.js file

Run the webpack — config. /webpack.js command

Index.html will simply import the generated colorgame.js.

Demo. Project source code