One, foreword

A scratch card module needs to be implemented in the project. When the project is finished, the scratch card module can be encapsulated. When it is used in the next new project, the efficiency of the project can be better improved, and of course, it can be better provided to other partners.

Source code address: github.com/ZENGzoe/vue… NPM package address: www.npmjs.com/package/vue…

The effects of scratch card components are as follows:

Two, the implementation of scratch card VUE components

The implementation of scratch card function can be divided into three steps:

1. Set up workflow

The workflow uses the VuE-CLI webPack-Simple template, which can meet the basic compilation requirements of components:

vue init webpack-simple vue-scratch-card
Copy the code

After execution, input package.json information according to the component.

Use sass? (y/N) y
Copy the code

In this project, I choose Use Sass.

Create the Packages directory under SRC to hold all the sub-components. There is only one scratch-card component in this component, so create a scratch-Card directory in packages to hold our scratch-card component. If there are other subcomponents, you can continue to add subcomponents in Packages, the final directory is as follows:

. ├ ─ ─ the README. Md ├ ─ ─ index. The HTML ├ ─ ─ node_modules ├ ─ ─ package - lock. Json ├ ─ ─ package. The json ├ ─ ─ the SRC │ ├ ─ ─ App. Vue │ ├ ─ ─ │ ├─ ├─ class.js │ ├─ class.js │ ├─ class.js │ ├─ class.js │ ├─ class.js │ ├─ ├─ garbage, ├─ garbage, ├─ garbage, ├─ garbage, │ ├─ garbage, ├─ garbage, ├─ garbage, ├─ garbage, ├─ garbage, ├─ garbage, ├─ garbage └ ─ ─ webpack. Config. JsCopy the code

To support components that can be imported using the tag

So we need to modify our configuration file webpack.config.js:

//webpack.config.js
 output: {
    path: path.resolve(__dirname, './dist'),
    publicPath: '/dist/',
    filename: 'vue-scratch-card.js',
    library : 'vue-scratch-card'// Set the module name to libraryTarget when require is used:'umd'//libraryTarget can set different UMD codes, such as CommonJS standard, AMD standard, or generate umdNamedDefine imported from script tags:true// Amd modules in UMD build process will be named, otherwise use anonymous define},Copy the code

Also, in order to preserve the packaged dist directory, you need to remove the dist directory in.gitignore.

2. Canvas achieves scratch card

Scratch cards are mainly implemented through Canvas. Generally, scratch cards are combined with lottery, so our DOM should contain the DOM that can display the result of lottery. The structure is as follows:

//scratch-card.vue
<template>
    <div :id='elementId' class='scratchCard'>
        <div class="result" v-show='showLucky'>
            <slot name='result'></slot>
            <img :src="resultImg" alt="" class="pic" />
        </div>
        <canvas id='scratchCanvas'></canvas>
    </div>
</template>
Copy the code

A

slot has been added so that the DOM of the raffle results can be customized when the component is called.

Next comes the logical part of implementing the scratch card.

The general JS structure is as follows:

//scratch-card.vue
    export default {
        name : 'vueScratchCard'.data() {return {
                supportTouch : false// Touch events: [], // Touch events or mouse events set startMoveHandler: null, // TouchStart or mouseDown events moveHandler: Null, // TouchMove or mousemove endMoveHandler: null, // TouchEnd or mouseend showLucky:false// Display hidden lucky draw resultsfirsttouch:true// If touchStart or mousedown}}, props: {elementId: {// Specifies the id attribute of the outermost DOM of the componenttype : String,
                default : 'scratch'}, moveRadius: {// scratch rangetype: Number, default: 15}, ratio: {// Ratio of the area to be scraped off. When this ratio is reached, other areas will be cleared automaticallytype: Number, default: 0.3}, startCallback: {// first callback callback functiontype : Function,
                default : function(){}}, clearCallback: {// Ratio ratio reached after the callback functiontype : Function ,
                default : function(){}}, coverColor: {// scratch card coverColortype : String,
                default : '#C5C5C5'}, coverImg: {// Scratch card mask imagetype: String,}, resultImg: {// The winning graphtype : String,
                default : 'https://raw.githubusercontent.com/ZENGzoe/imagesCollection/master/2018/default.jpg'
            }
        },
        mounted : function(){
        },
        methods : {
        }
    }
Copy the code

Before you start writing the logic, you need to consider component configurable properties and add them to props to make the use of components more flexible.

When the component is mounted to the instance, initialize the component and draw the Canvas.

//scratch-card.vue
...

mounted : function(){
    this.$nextTick(() => {
        this.init();
    })
},
methods : function(){
    init : function() {if(! this.isSupportCanvas){ alert('Current browser does not support Canvas');
            return;
        }
        const canvasWrap = document.getElementById(this.elementId);
        this.canvas =canvasWrap.querySelector('#scratchCanvas');
        this.ctx = this.canvas.getContext('2d');
        this.canvas.width = canvasWrap.clientWidth;
        this.canvas.height = canvasWrap.clientHeight;

        this.createCanvasStyle();
    },
    createCanvasStyle : function(){ var _this = this; // When passing coverImg, use the image first, otherwise use the color as the scratchcard maskif(this.coverImg){
            var coverImg = new Image();
            coverImg.src = this.coverImg;
            coverImg.onload = function(){ _this.ctx.drawImage(coverImg , 0 , 0 , _this.canvas.width , _this.canvas.height); }}else{ _this.ctx.fillStyle = _this.coverColor; _this. CTX. FillRect (0, 0, _this. Canvas. Width, _this. Canvas, height); }}}...Copy the code

When you want to add a cross-domain image to Canvas, you need to convert the image to Base64.

The events bound to the PC page are mousesDown, Mousemove, and Mouseup. The events bound to the mobile page are TouchStart, TouchMove, and TouchEnd. Therefore, you need to distinguish the events bound to different ends.

//scratch-card.vue
...
init : function(){
    ...
    this.bindEvent();
},
bindEvent : function() {if('ontouchstart' in window) this.supportTouch = true;
    this.events = this.supportTouch ? ['touchstart'.'touchmove'.'touchend'] : ['mousedown'.'mousemove'.'mouseup']; this.addEvent(); },...Copy the code

To reduce the length of the binding event addEvent concrete implementation, you can view the source code.

The effect of the scratch card wipe is achieved by the Canvas’s globalCompositeOperation property. Set globalCompositeOperation = “destination-Out” to make the area where the finger or mouse is aligned with the Canvas invisible. Can make the scratchcard wipe effect. Add a wipe effect to the event bound to TouchMove and Mousemove. The implementation is as follows:

moveEventHandler : function(e){
    e.preventDefault();
    
    e = this.supportTouch ? e.touches[0] : e;

    const canvasPos = this.canvas.getBoundingClientRect(),
          scrollT = document.documentElement.scrollTop || document.body.scrollTop,
          scrollL = document.documentElement.scrollLeft || document.body.scrollLeft,
           //获取鼠标或手指在canvas画布的位置
          mouseX = e.pageX - canvasPos.left - scrollL, 
          mouseY = e.pageY - canvasPos.top - scrollT;

    this.ctx.beginPath();
    this.ctx.fillStyle = '#FFFFFF';
    this.ctx.globalCompositeOperation = "destination-out";
    this.ctx.arc(mouseX, mouseY, this.moveRadius, 0, 2 * Math.PI);
    this.ctx.fill();
},
Copy the code

Each time the finger or mouse leaves, calculate the wipe area, and when the wipe area is greater than the agreed percentage of the Canvas, clear the entire Canvas. The calculation of the wipe area is equivalent to calculating whether there is any data on the pixels on the canvas, which can be obtained by getImageData method. The concrete implementation is as follows:

caleArea : function() {letPixels = this. CTX. GetImageData (0, 0, this. Canvas. Width, enclosing canvas. Height), transPixels = []; pixels.data.map((item , i) => { const pixel = pixels.data[i+3];if(pixel === 0){ transPixels.push(pixel); }})if(transpixels.length/pixels.data.length > this.ratio){this.ctx.clearrect (0,0,this.canvas.width, this.canvas.height); this.canvas.removeEventListener(this.events[0],this.startMoveHandler); this.canvas.removeEventListener(this.events[1] , this.moveHandler ,false);
        document.removeEventListener(this.events[2] , this.endMoveHandler , false); this.clearCallback(); }}Copy the code

Every time a finger or mouse leaves, the bound events are cleared so as not to contaminate events and occupy content in other areas.

So that’s all the scratch-card logic, and the next step is to package the scratch-card components as plug-ins.

3. Encapsulate it as a plug-in

To encapsulate a VUE component as a plug-in, there should be a public install method so that the plug-in can be called through vue.use (). See VUE’s official documentation for details.

Create a new index.js in the scratch-card directory that encapsulates the install method for scratchCard:

//scratch-card/index.js
import vueScratchCard from './scratch-card'

vueScratchCard.install = Vue => Vue.component(vueScratchCard.name , vueScratchCard);

if(typeof window ! = ='undefined' && window.Vue){
    window.Vue.use(vueScratchCard);
}

export default vueScratchCard;
Copy the code

At this point we have wrapped our child scratchcards. If there are other child components, we can continue to add them to the Packages directory and finally create index.js under the SRC directory to combine all the child components.

//src/index.js

import VueScratchCard from './packages/scratch-card/index.js';

const install = function(Vue , opts = {}){ Vue.component(VueScratchCard.name , VueScratchCard); } // Support the use of the tag <script> importedif(typeof window ! = ='undefined' && window.Vue){
    install(window.Vue);
}

export default {
    install ,
    VueScratchCard
}
Copy the code

This completes our component

Publish to NPM

Before publishing to NPM, modify package.json and set “private”:true, otherwise NPM will refuse to publish private packages. In addition, you need to add an entry file, “main”:”dist/vue-scratch-card.js”, which can be loaded when requiring or importing packages.

//package.json
  "private": false."main" : "dist/vue-scratch-card.js".Copy the code

The NPM release process is as follows:

1. Register an account with NPM

2. Log in to NPM and change the image address to NPM

npm login
Copy the code

3. Add user information and enter the account password

npm adduser
Copy the code

4. Release

npm publish 
Copy the code

After a successful publication, the published package can be searched in NPM.

Iv. Installation and use package

Then we can use it in direct installation

Installation:

npm install vue-scratch-card0 -S 
Copy the code

Use:

1. Use import

import ScratchCard from 'vue-scratch-card0'
Vue.use(ScratchCard)

<vue-scratch-card 
    element-id='scratchWrap': thewire = 0.5: move - the radius = 50 / >Copy the code

2. Reference with the tag

<vue-scratch-card 
    element-id='scratchWrap'
    :ratio=0.5
    :move-radius=50
    :start-callback=startCallback
    :clear-callback=clearCallback
    cover-color='#caa'
  />

<script src="./node_modules/vue/dist/vue.js"></script>
<script>
    new Vue({
      el : '#app'.data () {
        return {

        }
      },
      methods : {
        startCallback(){
          console.log('Lucky draw! ')},clearCallback(){
          console.log('Clear! ')
        }
      }
    })
</script>
Copy the code

Five, the summary

NPM: Package name too similar to existing packages, NPM: Package name too similar to existing packages, NPM: Package name too similar to existing packages It’s not easy