整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          小猿圈web前端之HTML5拖拽功能實(shí)現(xiàn)的拼圖游戲

          小猿圈web前端之HTML5拖拽功能實(shí)現(xiàn)的拼圖游戲

          時(shí)候玩的拼圖游戲,大家好記得嗎?有沒有想過自己做一個(gè)這樣的游戲呢,特別一些特殊的日子,送給他(她)呢。為了實(shí)現(xiàn)大家的想法,小猿圈web前端講師就講講利用HTML5拖拽功能實(shí)現(xiàn)的拼圖游戲

          具體代碼如下所示:

          <!--代碼如下,最下面給出了我測(cè)試用的9張250*250的圖片切片-->

          <!DOCTYPE html>

          <html>

          <head>

          <meta charset="utf-8">

          <title>drag拖拽</title>

          <style>

          .box{

          float: left;

          }

          img{

          width: 150px;

          height:150px;

          }

          #puzzle{

          font-size: 0;

          margin:80px auto;

          padding: 5px;

          width: 460px;

          }

          </style>

          </head>

          <body>

          <div id="puzzle">

          <div class="box"><img alt="1"></div>

          <div class="box"><img alt="2"></div>

          <div class="box"><img alt="3"></div>

          <div class="box"><img alt="4"></div>

          <div class="box"><img alt="5"></div>

          <div class="box"><img alt="6"></div>

          <div class="box"><img alt="7"></div>

          <div class="box"><img alt="8"></div>

          <div class="box"><img alt="9"></div>

          </div>

          <script>

          var image=document.getElementsByTagName("img");

          var box=document.getElementsByClassName("box");

          image.draggable=true;

          var source="";

          var nowImage;

          var nowImageBox;

          var thenImage;

          for(let i=0;i<image.length;i++){

          source="picture"+i+".jpg";

          image[i].setAttribute("src",source);

          image[i].onmousedown=function(){

          nowImage=this;

          nowImageBox=this.parentNode;

          }

          box[i].ondragover=function(event){

          event.preventDefault(); //去除ondragover事件的默認(rèn)行為,該行為默認(rèn)無法將數(shù)據(jù)或者元素放置到其他元素

          }

          box[i].ondrop=function(event){

          thenImage=box[i].childNodes[0];

          box[i].appendChild(nowImage);

          nowImageBox.appendChild(thenImage);

          }

          }

          </script>

          </body>

          </html>

          以上就是小猿圈web前端講師針對(duì)HTML5拖拽功能實(shí)現(xiàn)的拼圖游戲的講解,你有想過學(xué)習(xí)前端開發(fā)嘛,想學(xué)習(xí)前端可以到小猿圈去直接觀看,這里面從基礎(chǔ)到實(shí)戰(zhàn)的所有學(xué)習(xí)資料,可以滿足你提升自己,為你實(shí)現(xiàn)編程夢(mèng)想的起點(diǎn)。

          文收錄本頭條號(hào)已發(fā)表的經(jīng)典文章,并持續(xù)更新中,請(qǐng)大家關(guān)注。

          ES6/JavaScript的箭頭函數(shù)是怎樣將this與作用域聯(lián)系起來的,及什么是this詞法

          你所不知道的JavaScript動(dòng)態(tài)作用域,它與詞法作用域有什么區(qū)別和關(guān)聯(lián)?

          你寫過JavaScript的類嗎?你知道JavaScript的類的機(jī)制是什么樣的嗎?

          JavaScript中this是一種什么機(jī)制?又該如何的避免的二個(gè)坑?

          高級(jí)JavaScript程序員必須掌握的一個(gè)問題:如何解決回調(diào)的信任問題

          JavaScript異步:請(qǐng)先理解JavaScript的事件循環(huán)

          使用JavaScript+HTML5 Canvas實(shí)現(xiàn)的界面元素截屏功能

          區(qū)分初級(jí)和高級(jí)JavaScript程序員:是否理解JavaScript閉包(Closure)

          在JavaScript中使用LINQ:linq.js插件簡(jiǎn)介

          Processing.js和P5.js的功能簡(jiǎn)介和區(qū)別

          前端開發(fā)框架選型:Angular2/React/jQuery

          JavaScript語言的三種編程范式比較

          JavaScript的那些坑,你遇到過嗎?又該如何避免?

          常見面試題JavaScript語言中四種函數(shù)調(diào)用方式實(shí)例分析

          Ionic2、React、NativeScript比較

          JavaScript Promise實(shí)現(xiàn)任務(wù)的靈活調(diào)度

          一個(gè)支持PDF/文本/二進(jìn)制文件的實(shí)用的客戶端數(shù)字簽名框架

          jQuery實(shí)現(xiàn)純HTML/CSS的拼圖游戲

          阻止廣告:JavaScript實(shí)現(xiàn)Chrome插件實(shí)例分析

          JavaScript實(shí)現(xiàn)三維軌道燈動(dòng)畫

          使用 Angular 構(gòu)建 JavaScript 應(yīng)用

          利用jQuery實(shí)現(xiàn)多個(gè)ajax請(qǐng)求等待

          JavaScript代碼重構(gòu)

          SQL注入和跨站點(diǎn)腳本實(shí)例分析

          仿生智能算法之路徑智能尋找的HTML及JS動(dòng)畫實(shí)現(xiàn)代碼

          HTML5動(dòng)畫背后的數(shù)學(xué)-粒子群仿生算法簡(jiǎn)介

          HTML5及JS實(shí)現(xiàn)粒子群仿生動(dòng)畫代碼

          NodeJS、Java和PHP性能考量和若干參考結(jié)論

          WebGL、Asm.js和WebAssembly概念簡(jiǎn)介

          WebAssembly工作原理和JavaScript性能對(duì)比

          Async之流程控制

          由:之前看嗶哩嗶哩官網(wǎng)登錄的時(shí)候有一個(gè)拼圖驗(yàn)證碼,很好奇怎么去實(shí)現(xiàn)。然后就想著自己弄一個(gè)。先給大家看我的最終效果。后面再一點(diǎn)點(diǎn)拆解代碼。

          為什么想著寫這個(gè)功能呢,主要在于拼圖驗(yàn)證碼在前端這里會(huì)比較復(fù)雜并且深入。相比文字拼寫,12306的圖片驗(yàn)證碼都沒有拼圖驗(yàn)證碼對(duì)前端的要求來的復(fù)雜,和難。

          我總結(jié)下知識(shí)點(diǎn):

          1、彈窗功能

          2、彈窗基于元素定位

          3、元素拖動(dòng)

          4、canvas繪圖

          5、基礎(chǔ)邏輯

          一、彈窗和彈窗組件

          抱歉,這里我偷懶了直接用了elementUI的el-popover組件,所以小伙伴不懂的直接看elementUI官網(wǎng)的說明。

          我個(gè)人也研究和編寫了這塊的組件功能(基于popper.js)

          二、編寫基礎(chǔ)結(jié)構(gòu)

          這塊屬于html的基礎(chǔ)內(nèi)容,也就標(biāo)題黨了

          三、canvas繪制圖片

          1、canvas繪制外部img圖片

          代碼:

          let mainDom=document.querySelector("#codeImg");
          let bg=mainDom.getContext("2d");
          let width=mainDom.width;
          let height=mainDom.height;
          let blockDom=document.querySelector("#sliderBlock");
          let block=blockDom.getContext("2d");
          //重新賦值,讓canvas進(jìn)行重新繪制
          blockDom.height=height;
          mainDom.height=height;
          let imgsrc=require("../assets/images/back.jpg");
          let img=document.createElement("img");
          img.style.objectFit="scale-down";
          img.src=imgsrc;
          img.onload=function() {
           bg.drawImage(img, 0, 0, width, height);
           block.drawImage(img, 0, 0, width, height);
          };復(fù)制代碼
          

          這里我繪制了兩個(gè)canvas,因?yàn)橐粋€(gè)是背景一個(gè)是滑塊

          核心在于

          let mainDom=document.querySelector("#codeImg");
          let imgsrc=require("../assets/images/back.jpg");
          let bg=mainDom.getContext("2d");
          let img=document.createElement("img");
          img.onload=function() {
           bg.drawImage(img, 0, 0, width, height);
          };
          復(fù)制代碼
          

          2、canvas繪制滑塊部分

          就是這個(gè)圖,這個(gè)有一些知識(shí)點(diǎn),不難,但是很復(fù)雜。

          代碼部分:

          drawBlock(ctx, xy={ x: 254, y: 109, r: 9 }, type) {
           let x=xy.x,
           y=xy.y,
           r=xy.r,
           w=40;
           let PI=Math.PI;
           //繪制
           ctx.beginPath();
           //left
           ctx.moveTo(x, y);
           //top
           ctx.arc(x + (w + 5) / 2, y, r, -PI, 0, true);
           ctx.lineTo(x + w + 5, y);
           //right
           ctx.arc(x + w + 5, y + w / 2, r, 1.5 * PI, 0.5 * PI, false);
           ctx.lineTo(x + w + 5, y + w);
           //bottom
           ctx.arc(x + (w + 5) / 2, y + w, r, 0, PI, false);
           ctx.lineTo(x, y + w);
           ctx.arc(x, y + w / 2, r, 0.5 * PI, 1.5 * PI, true);
           ctx.lineTo(x, y);
           //修飾,沒有會(huì)看不出效果
           ctx.lineWidth=1;
           ctx.fillStyle="rgba(255, 255, 255, 0.5)";
           ctx.strokeStyle="rgba(255, 255, 255, 0.5)";
           ctx.stroke();
           ctx[type]();
           ctx.globalCompositeOperation="xor";
          }復(fù)制代碼
          

          解釋下:

          參數(shù)是傳入canvas對(duì)象

          x,y軸數(shù)據(jù)

          剪切還是填充的canvas函數(shù)(fill,clip)

          繪制難點(diǎn):(很重要,不然你沒法理解它怎么繪制的)

          繪制主要是需要理解這里的繪制是根據(jù)你設(shè)置一個(gè)起始點(diǎn)坐標(biāo),然后你繪制第二次的時(shí)候線就會(huì)連接到第二個(gè)點(diǎn),依次連接最后回到原點(diǎn)就形成一個(gè)完整的圖形。

          用的是arc參數(shù),主要是看這個(gè)圖

          fill是用于填充繪制的部分,clip是裁剪出繪制的部分,利用這個(gè)就可以出現(xiàn)一個(gè)扣掉的圖片和一個(gè)裁剪出來的圖片。

          完成之后就是我的那個(gè)函數(shù)了。大家可以直接拿去用。

          3、讓元素跟隨鼠標(biāo)點(diǎn)擊之后滑動(dòng)

          這里其實(shí)原理非常簡(jiǎn)單,就是有一個(gè)注意點(diǎn)。

          原理:

          鼠標(biāo)點(diǎn)擊之后記錄當(dāng)前坐標(biāo),然后隨著(mousemove)滾動(dòng)的時(shí)候修改元素的left和top值就行了。

          還有一點(diǎn)就是鼠標(biāo)快速滑動(dòng)會(huì)導(dǎo)致丟失滑動(dòng)效果,這里需要用document,不能是元素級(jí)別的監(jiān)聽。

          元素上面我只需要鑒定按下mousedown

          代碼:

          //鼠標(biāo)按下
          drag(e) {
           console.log("鼠標(biāo)按下", e);
           let dom=e.target; //dom元素
           let slider=document.querySelector("#sliderBlock"); //滑塊dom
           const downCoordinate={ x: e.x, y: e.y };
           //正確的滑塊數(shù)據(jù)
           let checkx=Number(this.slider.mx) - Number(this.slider.bx);
           //x軸數(shù)據(jù)
           let x=0;
           const move=moveEV=> {
           x=moveEV.x - downCoordinate.x;
           //y=moveEV.y - downCoordinate.y;
           if (x >=251 || x <=0) return false;
           dom.style.left=x + "px";
           //dom.style.top=y + "px";
           slider.style.left=x + "px";
           };
           const up=()=> {
           document.removeEventListener("mousemove", move);
           document.removeEventListener("mouseup", up);
           dom.style.left="";
           console.log(x, checkx);
           let max=checkx - 5;
           let min=checkx - 10;
           //允許正負(fù)誤差1
           if ((max >=x && x >=min) || x===checkx) {
           console.log("滑動(dòng)解鎖成功");
           this.puzzle=true;
           this.tips="驗(yàn)證成功";
           setTimeout(()=> {
           this.visible=false;
           }, 500);
           } else {
           console.log("拼圖位置不正確");
           this.tips="驗(yàn)證失敗,請(qǐng)重試";
           this.puzzle=false;
           this.canvasInit();
           }
           };
           document.addEventListener("mousemove", move);
           document.addEventListener("mouseup", up);
          }復(fù)制代碼
          

          4、總結(jié)

          核心點(diǎn)比較多,寫過之后發(fā)現(xiàn)不難,關(guān)鍵在于去寫

          個(gè)人該頁面git地址:https://github.com/ht-sauce/dream

          該頁面處于項(xiàng)目的

          路由訪問為:http://localhost:8080/consumer

          5、完整的頁面代碼

          <template>
           <div id="login">
           <el-form class="loginFrom" :model="logindata" :rules="rules" ref="ruleForm">
           <el-form-item class="login-item">
           <h1 class="login-title">海天醬油登錄中心</h1>
           </el-form-item>
           <el-form-item prop="userName">
           <el-input
           class="login-inputorbuttom"
           prefix-icon="el-icon-user"
           placeholder="登錄名"
           v-model="logindata.userName"
           ></el-input>
           </el-form-item>
           <el-form-item prop="password">
           <el-input
           class="login-inputorbuttom"
           prefix-icon="el-icon-lock"
           placeholder="密碼"
           v-model="logindata.password"
           ></el-input>
           </el-form-item>
           <!--<el-form-item prop="verificationCode">
           <el-input
           class="login-inputorbuttom"
           v-model="logindata.verificationCode"
           ></el-input>
           </el-form-item>-->
           <el-form-item class="login-item">
           <el-button
           class="login-inputorbuttom login-bottom"
           type="primary"
           v-popover:popover
           @click="visible=!visible"
           >登 錄</el-button
           >
           </el-form-item>
           </el-form>
           <!--驗(yàn)證碼彈窗-->
           <el-popover
           popper-class="slidingPictures"
           ref="popover"
           trigger="manual"
           v-model="visible"
           >
           <div class="sliding-pictures">
           <div class="vimg">
           <canvas id="sliderBlock"></canvas>
           <canvas id="codeImg"></canvas>
           </div>
           <div class="slider">
           <div class="track" :class="{ pintuTrue: puzzle }">
           {{ tips }}
           </div>
           <div class="button el-icon-s-grid" @mousedown.prevent="drag"></div>
           </div>
           <div class="operation">
           <span
           title="關(guān)閉驗(yàn)證碼"
           @click="visible=false"
           class="el-icon-circle-close"
           ></span>
           <span
           title="刷新驗(yàn)證碼"
           @click="canvasInit"
           class="el-icon-refresh-left"
           ></span>
           </div>
           </div>
           </el-popover>
           </div>
          </template>
          <script>
          export default {
           name: "login",
           data() {
           return {
           tips: "拖動(dòng)左邊滑塊完成上方拼圖",
           logindata: {
           userName: "",
           password: "",
           verificationCode: ""
           },
           rules: {},
           visible: false,
           //滑塊x軸數(shù)據(jù)
           slider: {
           mx: 0,
           bx: 0
           },
           //拼圖是否正確
           puzzle: false
           };
           },
           watch: {
           visible(e) {
           if (e===true) {
           this.canvasInit();
           this.puzzle=false;
           }
           }
           },
           beforeCreate() {},
           created() {},
           beforeMount() {},
           mounted() {},
           methods: {
           //拼圖驗(yàn)證碼初始化
           canvasInit() {
           //生成指定區(qū)間的隨機(jī)數(shù)
           const random=(min, max)=> {
           return Math.floor(Math.random() * (max - min + 1) + min);
           };
           //x: 254, y: 109
           let mx=random(127, 244),
           bx=random(10, 128),
           y=random(10, 99);
           this.slider={ mx, bx };
           this.draw(mx, bx, y);
           },
           //鼠標(biāo)按下
           drag(e) {
           console.log("鼠標(biāo)按下", e);
           let dom=e.target; //dom元素
           let slider=document.querySelector("#sliderBlock"); //滑塊dom
           const downCoordinate={ x: e.x, y: e.y };
           //正確的滑塊數(shù)據(jù)
           let checkx=Number(this.slider.mx) - Number(this.slider.bx);
           //x軸數(shù)據(jù)
           let x=0;
           const move=moveEV=> {
           x=moveEV.x - downCoordinate.x;
           //y=moveEV.y - downCoordinate.y;
           if (x >=251 || x <=0) return false;
           dom.style.left=x + "px";
           //dom.style.top=y + "px";
           slider.style.left=x + "px";
           };
           const up=()=> {
           document.removeEventListener("mousemove", move);
           document.removeEventListener("mouseup", up);
           dom.style.left="";
           console.log(x, checkx);
           let max=checkx - 5;
           let min=checkx - 10;
           //允許正負(fù)誤差1
           if ((max >=x && x >=min) || x===checkx) {
           console.log("滑動(dòng)解鎖成功");
           this.puzzle=true;
           this.tips="驗(yàn)證成功";
           setTimeout(()=> {
           this.visible=false;
           }, 500);
           } else {
           console.log("拼圖位置不正確");
           this.tips="驗(yàn)證失敗,請(qǐng)重試";
           this.puzzle=false;
           this.canvasInit();
           }
           };
           document.addEventListener("mousemove", move);
           document.addEventListener("mouseup", up);
           },
           draw(mx=200, bx=20, y=50) {
           let mainDom=document.querySelector("#codeImg");
           let bg=mainDom.getContext("2d");
           let width=mainDom.width;
           let height=mainDom.height;
           let blockDom=document.querySelector("#sliderBlock");
           let block=blockDom.getContext("2d");
           //重新賦值,讓canvas進(jìn)行重新繪制
           blockDom.height=height;
           mainDom.height=height;
           let imgsrc=require("../assets/images/back.jpg");
           let img=document.createElement("img");
           img.style.objectFit="scale-down";
           img.src=imgsrc;
           img.onload=function() {
           bg.drawImage(img, 0, 0, width, height);
           block.drawImage(img, 0, 0, width, height);
           };
           let mainxy={ x: mx, y: y, r: 9 };
           let blockxy={ x: bx, y: y, r: 9 };
           this.drawBlock(bg, mainxy, "fill");
           this.drawBlock(block, blockxy, "clip");
           },
           //繪制拼圖
           drawBlock(ctx, xy={ x: 254, y: 109, r: 9 }, type) {
           let x=xy.x,
           y=xy.y,
           r=xy.r,
           w=40;
           let PI=Math.PI;
           //繪制
           ctx.beginPath();
           //left
           ctx.moveTo(x, y);
           //top
           ctx.arc(x + (w + 5) / 2, y, r, -PI, 0, true);
           ctx.lineTo(x + w + 5, y);
           //right
           ctx.arc(x + w + 5, y + w / 2, r, 1.5 * PI, 0.5 * PI, false);
           ctx.lineTo(x + w + 5, y + w);
           //bottom
           ctx.arc(x + (w + 5) / 2, y + w, r, 0, PI, false);
           ctx.lineTo(x, y + w);
           ctx.arc(x, y + w / 2, r, 0.5 * PI, 1.5 * PI, true);
           ctx.lineTo(x, y);
           //修飾,沒有會(huì)看不出效果
           ctx.lineWidth=1;
           ctx.fillStyle="rgba(255, 255, 255, 0.5)";
           ctx.strokeStyle="rgba(255, 255, 255, 0.5)";
           ctx.stroke();
           ctx[type]();
           ctx.globalCompositeOperation="xor";
           }
           }
          };
          </script>
          <style>
          .slidingPictures {
           padding: 0;
           width: 300px;
           border-radius: 2px;
          }
          </style>
          <style scoped lang="scss">
          #login {
           display: flex;
           flex-flow: row;
           justify-content: flex-end;
           align-items: center;
           width: 100%;
           height: 100%;
           background-image: url("../assets/images/back.jpg");
           background-size: 100% 100%;
           .loginFrom {
           width: 300px;
           margin-top: -10vw;
           margin-right: 10vw;
           .login-item {
           display: flex;
           justify-content: center;
           align-items: center;
           }
           .login-title {
           color: #ffffff;
           font-size: 16px;
           margin-bottom: 10px;
           }
           .login-bottom {
           margin-top: 15px;
           }
           .login-bottom:hover {
           background: rgba(28, 136, 188, 0.5);
           }
           .login-bottom:active {
           background: rgba(228, 199, 200, 0.5);
           }
           /deep/.login-inputorbuttom {
           height: 40px;
           width: 300px;
           background: rgba(57, 108, 158, 0.5);
           border-radius: 20px;
           border: #396c9e 1px solid;
           font-size: 14px;
           color: #ffffff;
           .el-input--small,
           .el-input__inner {
           line-height: 43px;
           border: none;
           color: #ffffff;
           font-size: 14px;
           height: 40px;
           border-radius: 20px;
           background: transparent;
           text-align: center;
           }
           .el-input__icon {
           line-height: 40px;
           font-size: 16px;
           }
           }
           }
          }
          /*該樣式最終是以彈窗插入*/
          .sliding-pictures {
           width: 100%;
           .vimg {
           width: 100%;
           height: 170px;
           #codeImg,
           #sliderBlock {
           padding: 7px 7px 0 7px;
           width: inherit;
           height: inherit;
           }
           #codeImg {
           //display: none;
           }
           #sliderBlock {
           position: absolute;
           z-index: 4000;
           }
           }
           .slider {
           width: 100%;
           height: 65px;
           border-bottom: #c7c9d0 1px solid;
           display: flex;
           align-items: center;
           justify-content: flex-start;
           .track {
           margin-left: 7px;
           width: 286px;
           height: 38px;
           background: rgba(28, 136, 188, 0.5);
           border-radius: 25px;
           font-size: 14px;
           line-height: 38px;
           padding-right: 15px;
           padding-left: 70px;
           }
           .pintuTrue {
           background: #67c23a;
           color: #ffffff;
           }
           .button {
           position: absolute;
           width: 50px;
           height: 50px;
           line-height: 48px;
           background: #ffffff;
           box-shadow: #b9bdc8 0 0 3px;
           border-radius: 50%;
           left: 7px;
           text-align: center;
           font-size: 28px;
           color: #3e5d8b;
           &:hover {
           color: #2181bd;
           }
           }
           }
           .operation {
           width: 100%;
           height: 40px;
           > span {
           color: #9fa3ac;
           display: inline-block;
           width: 40px;
           font-size: 25px;
           line-height: 40px;
           text-align: center;
           &:hover {
           background: #e2e8f5;
           }
           }
           }
          }
          </style>
          

          最后

          喜歡的可以點(diǎn)個(gè)關(guān)注嗎,小可樂也不容易呢


          主站蜘蛛池模板: 国产免费一区二区三区免费视频| 久久久一区二区三区| 久久一区二区三区精华液使用方法| 香蕉视频一区二区三区| 精品成人一区二区三区免费视频 | 精品一区二区三区在线播放| 精品一区二区三区四区电影| 中文字幕aⅴ人妻一区二区 | 精品国产乱码一区二区三区| 久久久国产精品无码一区二区三区| 国产日韩视频一区| 精品国产毛片一区二区无码| 无码日韩精品一区二区三区免费 | 国产成人精品无码一区二区老年人 | 国产精品日韩一区二区三区| 高清无码一区二区在线观看吞精| 国产福利酱国产一区二区| 成人欧美一区二区三区在线视频| 亚洲第一区二区快射影院| 海角国精产品一区一区三区糖心 | 国产午夜福利精品一区二区三区 | 久久久精品人妻一区二区三区蜜桃| 欧美日韩国产免费一区二区三区 | 性色AV一区二区三区| 国产精品一区二区久久精品无码 | 日韩高清国产一区在线| 亚洲av乱码一区二区三区按摩| 夜色阁亚洲一区二区三区| 麻豆AV一区二区三区| 日本一区二区三区中文字幕| 日韩人妻无码一区二区三区久久 | 中文字幕精品一区二区三区视频| 亚欧免费视频一区二区三区| 亚洲一区二区三区免费| 亚洲综合色一区二区三区| 亚洲无人区一区二区三区| 免费无码一区二区| 蜜芽亚洲av无码一区二区三区| 久久国产一区二区| 国产伦精品一区二区免费 | 亚洲色一区二区三区四区|