# canvas
## 1.簡介
HTML5 的新特性:在添加完成 `canvas` 元素后,通過 `js` 腳本繪制圖案。
## 2.驗證碼案例

案例說明:在用戶登錄時提供驗證碼功能(登錄錯誤超過幾次之后顯示,設置驗證碼的實效時間)
案例環境:vue
參考文檔:[登錄隨機驗證碼的實現](https://www.cnblogs.com/moning/p/7868731.html)、[如何給localStorage設置一個過期時間?](https://blog.csdn.net/weixin_43254766/article/details/83618630)
### 2.1實現思路:
使用canvas繪制驗證碼
localstorage中存儲密碼錯誤次數及第一次密碼錯誤時間
賬號或密碼驗證錯誤時從緩存中讀取錯誤次數
### 2.2具體代碼
驗證碼頁面元素canvas屬性方法設置
~~~
<li class="verification" v-if="showVerify">
? ? ? ? ?<input @blur="handleVerify" type="text" v-model.trim="verification" placeholder="請輸入驗證碼" />
? ? ? ? ?<Icon title="刷新驗證碼" @click="drawVerification" class="icon-refresh" type="md-refresh" />
? ? ? ? ?<canvas title="點擊刷新" @click.prevent="drawVerification" id="canvas" width="150px" height="40px"></canvas>
? ? ? ?</li>
~~~
繪制驗證碼方法
~~~
// 繪制驗證碼方法
drawVerification() {
?const canvas = document.getElementById("canvas");
?const str =
? ? ? ?"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
?let verifyVal = ""; // 初始定義隨機數
?const num = 4; // 設置隨機數個數
?const width = canvas.clientWidth;
?const height = canvas.clientHeight;
?const ctx = canvas.getContext("2d");
?// 設置、填充畫布底板
?ctx.fillStyle = randomColor(180, 240);
?ctx.fillRect(0, 0, width, height);
?// 繪制隨機驗證碼
?for (let i = 0; i < num; i++) {
? ?const x = (width / num) * i + 15;
? ?const y = randomNum(height / 1.8, height / 1.2);
? ?const deg = randomNum(-45, 45);
? ?const txt = str[randomNum(0, str.length)];
? ?verifyVal += txt; // 獲取一個隨機數
? ?ctx.fillStyle = randomColor(10, 100); // 字體填充隨機顏色
? ?ctx.font = randomNum(18, 25) + "px SimHei"; // 設置字體
? ?ctx.translate(x, y); // 將當前xy坐標作為原始坐標
? ?ctx.rotate((deg * Math.PI) / 180); // 旋轉隨機角度
? ?ctx.fillText(txt, 0, 0); // 繪制文本時以當前坐標為起點
? ?ctx.rotate((-deg * Math.PI) / 180); // 重置文本旋轉角度
? ?ctx.translate(-x, -y); // 將坐標系統重置為原始坐標
}
?// 繪制不規則線條
?for (let i = 0; i < num; i++) {
? ?// 定義畫筆顏色
? ?ctx.strokeStyle = randomColor(90, 180);
? ?ctx.beginPath();
? ?// 定義線條路徑
? ?ctx.moveTo(randomNum(0, width), randomNum(0, height));
? ?ctx.lineTo(randomNum(0, width), randomNum(0, height));
? ?// 按路徑繪制
? ?ctx.stroke();
}
?// 繪制圓點
?for (let i = 0; i < num * 10; i++) {
? ?ctx.fillStyle = randomColor(0, 255);
? ?ctx.beginPath();
? ?ctx.arc(randomNum(0, width), randomNum(0, height), 1, 0, 2 * Math.PI);
? ?ctx.fill();
}
?this.verifyVal = verifyVal;
}
~~~
登錄驗證
~~~
async login() {
?// ...
?try {
? ?const res = await loginCheck(userInfo);
? ?// ...
? ?localStorageUtils.remove("failureTimes");
? ?localStorageUtils.remove("failureTimes__expired__");
} catch (err) {
? ?const failureTimes = (localStorageUtils.get("failureTimes") || 0) + 1;
? ?// 登錄驗證錯誤超過4次時,顯示并繪制
? ?if (failureTimes > 4) {
? this.showVerify = true;
? if (this.verification) {
? this.verification = "";
? }
? await this.$nextTick();
? this.drawVerification();
? }
? ?if (failureTimes === 1) {
? const userInfo = localStorageUtils.get("userInfo");
? if (userInfo && userInfo.password) {
? ? ? ?// 若選擇了記住密碼,首次登錄驗證不通過時將密碼清空
? ? ? ?localStorageUtils.save("userInfo", {
? ? ? ? ?loginName: userInfo.loginName
? ? ? });
? ? }
? }
? ?// 緩存密碼錯誤次數與當前錯誤時間
? ?localStorageUtils.save("failureTimes", failureTimes, true);
}
}
~~~
緩存控制
~~~
export default {
?save(key, value, expired = false) {
? ?this.remove(key);
? ?if (expired) {
? ? ?localStorage.setItem(
? ? ? ?`${key}__expired__`,
? ? ? ?Date.now() + 1000 * 60 * 60 * 3
? ? );
? }
? ?localStorage.setItem(key, JSON.stringify(value));
},
?get(key) {
? ?const expired = localStorage.getItem(`${key}__expired__`);
? ?if (expired && Date.now() >= expired) {
? ? ?// 超出時間期限
? ? ?this.remove(key);
? ? ?this.remove(`${key}__expired__`);
? ? ?return;
? }
? ?const value = localStorage.getItem(key);
? ?if (value) {
? ? ?try {
? ? ? ?return value ? JSON.parse(value) : null;
? ? } catch (error) {
? ? ? ?// JSON.parse("xxx") 非對象字符串出錯,直接返回對應的字符串
? ? ? ?return value ? value : null;
? ? }
? } else {
? ? ?return;
? }
},
?remove(key) {
? ?localStorage.removeItem(key);
},
?removeAll() {
? ?localStorage.clear();
}
};
~~~