Canvas實現(xiàn)數(shù)字雨和放大鏡效果的代碼示例
正文
還是先來看看效果
- 數(shù)字雨
- 放大鏡
數(shù)字雨
我認(rèn)為數(shù)字雨的核心在于單條數(shù)字雨生成
和數(shù)字雨移動
事前先準(zhǔn)備幾個函數(shù),方便我們的操作,隨機數(shù)可以用來生成數(shù)字雨的坐標(biāo),文字的大小以及移動的速度。
/** 生成隨機數(shù)*/ const createRandomNum = useCallback((min: number, max: number) => { return Math.floor(Math.random() * (max - min + 1)) + min; }, []) /** * @description 創(chuàng)建01字符串 * @returns 01字符串 */ function generateRandomString() { const characters = '01'; const len = Math.floor(Math.random() * (60 - 45 + 1)) + 45; let randomString = ''; for (let i = 0; i < len; i++) { const randomIndex = Math.floor(Math.random() * characters.length); randomString += characters[randomIndex]; } return randomString; }
生成單條數(shù)字雨
單條的樣式就出來了
由于我們接下來要對它進(jìn)行移動,那我們必須記錄數(shù)字雨的相關(guān)信息,然后對坐標(biāo)進(jìn)行修改,最后再繪制就可以了。
interface List { x: number, y: number, text: string, fontSize: number, width: number, speed: number, }
在我們每生成一條數(shù)字雨時就去記錄對應(yīng)數(shù)據(jù)
再結(jié)合requestAnimationFrame
,我們的動畫效果就出來了
放大鏡
放大鏡比數(shù)字雨還簡單,一共就兩步,獲取有效的鼠標(biāo)坐標(biāo)
和drawImage切圖并繪制
代碼
數(shù)字雨
import { useState, useEffect, useRef, useMemo, useCallback } from 'react' import './DigitalRain.scss' interface List { x: number, y: number, text: string, fontSize: number, width: number, speed: number, } export default function Rain() { const canvasDom = useRef<any>(null) const canvasCtx = useRef<any>(null) const [width, setWidth] = useState(0) const [height, setHeight] = useState(0) /** 數(shù)字雨總數(shù)*/ const amount = useRef(100); /** 已存在的數(shù)字雨*/ const dataList = useRef<List[]>([]) const animation = useRef<any>(null) /** 設(shè)置canvas的寬高*/ const setCanvasSize = useCallback(() => { const screenWidth = window.innerWidth; const screenHeight = window.innerHeight; setHeight(screenHeight) setWidth(screenWidth) }, []) /** 生成隨機數(shù)*/ const createRandomNum = useCallback((min: number, max: number) => { return Math.floor(Math.random() * (max - min + 1)) + min; }, []) useEffect(() => { if (canvasDom.current === null) { return } setCanvasSize() canvasCtx.current = canvasDom.current.getContext('2d'); window.addEventListener('resize', setCanvasSize) window.addEventListener('scroll', setCanvasSize) }, []) useEffect(() => { canvasCtx.current.fillStyle = 'black'; canvasCtx.current.fillRect(0, 0, width, height); cancelAnimationFrame(animation.current); draw() }, [width, height]) /** * @description 創(chuàng)建01字符串 * @returns 01字符串 */ function generateRandomString() { const characters = '01'; const len = Math.floor(Math.random() * (60 - 45 + 1)) + 45; let randomString = ''; for (let i = 0; i < len; i++) { const randomIndex = Math.floor(Math.random() * characters.length); randomString += characters[randomIndex]; } return randomString; } /** 單條數(shù)字雨生成*/ const drawSeparateLine = useCallback((fontSize: number, width: number, x: number, y: number, text: string) => { canvasCtx.current.font = `bold ${fontSize}px Arial` let grd = canvasCtx.current.createLinearGradient(x, y, x + width, y); grd.addColorStop(0, "aqua"); grd.addColorStop(1, "transparent"); canvasCtx.current.fillStyle = grd canvasCtx.current.shadowColor = "aqua"; // 設(shè)置陰影顏色 canvasCtx.current.shadowBlur = 20; // 設(shè)置陰影的模糊程度 canvasCtx.current.fillText(text, x, y) }, []) /** * @description 記錄數(shù)據(jù) * @param x 起始坐標(biāo) {number} * @param y 起始坐標(biāo) {number} * @param text 文字 {string} * @returns */ const recordData = useCallback((x: number, y: number, text: string) => { const fontSize = createRandomNum(17, 24) const speed = createRandomNum(2, 4) let textOne = canvasCtx.current.measureText(text); drawSeparateLine(fontSize, textOne.width, x, y, text) dataList.current.push({ x, y, text, fontSize, width: textOne.width, speed }) }, []) /** 畫整個頁面*/ const draw = useCallback(() => { canvasCtx.current.clearRect(0, 0, width, height) canvasCtx.current.fillStyle = 'black'; canvasCtx.current.fillRect(0, 0, width, height); /** 移動*/ for (let i = 0; i < dataList.current.length; i++) { let item = dataList.current[i]; drawSeparateLine(item.fontSize, item.width, item.x - item.speed, item.y, item.text) dataList.current[i] = { ...dataList.current[i], x: item.x - item.speed } } /** 增加新的*/ const maxWidth = window.innerWidth * 3; const minWidth = window.innerWidth; const maxHeight = window.innerHeight; const minHeight = 0; for (let i = 0; i < amount.current - dataList.current.length; i++) { let x = createRandomNum(minWidth, maxWidth) let y = createRandomNum(minHeight, maxHeight) recordData(x, y, generateRandomString()) } /** 去除舊的*/ let list: number[] = []; for (let i = 0; i < dataList.current.length; i++) { if (dataList.current[i].x + dataList.current[i].width <= 0) { list.push(i) } } dataList.current = dataList.current.filter((item, ind) => { return !list.includes(ind) }) animation.current = requestAnimationFrame(draw); }, [width, height]) return ( <> <div className='rain'> <canvas ref={canvasDom} width={width} height={height} ></canvas> </div> </> ) }
放大鏡
/** 放大鏡*/ import { useState, useEffect, useRef, useMemo, useCallback } from 'react' import img1 from '../assets/imgs/1.jpg' import './MagnifyingGlass.scss' export default function Canvas() { const frame = useRef<any>(null) const canvasDom = useRef<any>(null) const magnifyCanvasDom = useRef<any>(null) const canvasCtx = useRef<any>(null) const magnifyCanvasCtx = useRef<any>(null) const magnifyingGlassSize = useRef(40) const [top, setTop] = useState(0); const [left, setLeft] = useState(0); const initLocation = useRef<any>({ x: 0, y: 0, minX: 0, maxX: 0, minY: 0, maxY: 0, size: 0, }) const setInitPointer = useCallback(() => { let info = canvasDom.current.getBoundingClientRect() initLocation.current = { x: info.x, y: info.y, minX: info.x, maxX: info.x + info.width - magnifyingGlassSize.current, minY: info.y, maxY: info.y + info.height - magnifyingGlassSize.current, } }, []) /** 初始化,渲染圖片*/ useEffect(() => { if (canvasDom.current == null) { return } canvasCtx.current = (canvasDom.current).getContext('2d'); magnifyCanvasCtx.current = (magnifyCanvasDom.current).getContext('2d'); setInitPointer() let img = new Image(); img.src = img1; img.onload = () => { const canvasWidth = canvasDom.current.width; const canvasHeight = canvasDom.current.height; const imageWidth = img.width; const imageHeight = img.height; const scale = Math.min(canvasWidth / imageWidth, canvasHeight / imageHeight); const scaledWidth = imageWidth * scale; const scaledHeight = imageHeight * scale; canvasCtx.current.drawImage(img, 0, 0, scaledWidth, scaledHeight) magnifyCanvasCtx.current.drawImage( canvasDom.current, 0, 0, magnifyingGlassSize.current, magnifyingGlassSize.current, 0, 0, 300, 300 ); } frame.current.addEventListener('mousemove', onMousemove) window.addEventListener('resize', setInitPointer) window.addEventListener('scroll', setInitPointer) return () => { frame.current.removeEventListener('mousemove', onMousemove) window.removeEventListener('resize', setInitPointer) window.removeEventListener('scroll', setInitPointer) } }, []) const onMousemove = useCallback((e: MouseEvent) => { let x = e.x; let y = e.y; let dataY = y - initLocation.current.y - magnifyingGlassSize.current / 2; //判斷邊界 if (dataY < initLocation.current.minY) { dataY = initLocation.current.minY } else if (dataY > initLocation.current.maxY) { dataY = initLocation.current.maxY } setTop(dataY) //判斷邊界 let dataX = x - initLocation.current.x - magnifyingGlassSize.current / 2; if (dataX < initLocation.current.minX) { dataX = initLocation.current.minX } else if (dataX > initLocation.current.maxX) { dataX = initLocation.current.maxX } setLeft(dataX) /** 切圖*/ magnifyCanvasCtx.current.drawImage( canvasDom.current, dataX, dataY, magnifyingGlassSize.current, magnifyingGlassSize.current, 0, 0, 300, 300 ); }, []) return ( <> <div ref={frame} style={{ display: 'inline-block' }}> <canvas className='glass' ref={canvasDom} width={300} height={300}> </canvas> <div style={{ position: 'fixed', zIndex: 0, top, left, width: `${magnifyingGlassSize.current}px`, height: `${magnifyingGlassSize.current}px`, background: 'yellow', opacity: '.2' }}> </div> </div> <canvas ref={magnifyCanvasDom} width={300} height={300}> </canvas> </> ) }
結(jié)語
分享結(jié)束
到此這篇關(guān)于Canvas實現(xiàn)數(shù)字雨和放大鏡效果的代碼示例的文章就介紹到這了,更多相關(guān)Canvas數(shù)字雨和放大鏡效果內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
javascript中mouseenter與mouseover的異同
javascript中mouseover和mouseenter的區(qū)別主要在于監(jiān)聽對象的子元素是否觸發(fā)事件。mouseover:鼠標(biāo)移入監(jiān)聽對象中,或者從監(jiān)聽對象的一個子元素移入另一個子元素中時觸發(fā)該事件。mouseenter:鼠標(biāo)移入監(jiān)聽對象時觸發(fā),在監(jiān)聽對象內(nèi)移動不會觸發(fā)。2017-06-06解決layui上傳文件提示上傳異常,實際文件已經(jīng)上傳成功的問題
今天小編就為大家分享一篇解決layui上傳文件提示上傳異常,實際文件已經(jīng)上傳成功的問題。具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08