最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Canvas實現(xiàn)數(shù)字雨和放大鏡效果的代碼示例

 更新時間:2023年07月27日 08:23:50   作者:卸任  
這篇文章主要介紹了如何Canvas實現(xiàn)數(shù)字雨和放大鏡效果,文中有完整的代碼示例,文章通過代碼介紹的非常清楚,感興趣的小伙伴跟著小編一起來看看吧

正文

還是先來看看效果

  • 數(shù)字雨

  • 放大鏡

數(shù)字雨

我認為數(shù)字雨的核心在于單條數(shù)字雨生成數(shù)字雨移動

事前先準備幾個函數(shù),方便我們的操作,隨機數(shù)可以用來生成數(shù)字雨的坐標,文字的大小以及移動的速度。

  /** 生成隨機數(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ù)字雨

單條的樣式就出來了

由于我們接下來要對它進行移動,那我們必須記錄數(shù)字雨的相關(guān)信息,然后對坐標進行修改,最后再繪制就可以了。

interface List {
  x: number,
  y: number,
  text: string,
  fontSize: number,
  width: number,
  speed: number,
}

在我們每生成一條數(shù)字雨時就去記錄對應數(shù)據(jù)

再結(jié)合requestAnimationFrame,我們的動畫效果就出來了

放大鏡

放大鏡比數(shù)字雨還簡單,一共就兩步,獲取有效的鼠標坐標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)
  /** 設置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";  // 設置陰影顏色
    canvasCtx.current.shadowBlur = 20;  // 設置陰影的模糊程度
    canvasCtx.current.fillText(text, x, y)
  }, [])
  /**
  * @description 記錄數(shù)據(jù)
  * @param x 起始坐標 {number} 
  * @param y 起始坐標 {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)文章

  • Bootstrap CSS組件之下拉菜單(dropdown)

    Bootstrap CSS組件之下拉菜單(dropdown)

    這篇文章主要為大家詳細介紹了Bootstrap CSS組件之下拉菜單(dropdown),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-12-12
  • echarts圖表無數(shù)據(jù)/空數(shù)據(jù)如何展示"暫無數(shù)據(jù)"

    echarts圖表無數(shù)據(jù)/空數(shù)據(jù)如何展示"暫無數(shù)據(jù)"

    在開發(fā)echarts的時候我們不得不考慮數(shù)據(jù)為空的情況,其實有很多種解決辦法,下面這篇文章主要給大家介紹了關(guān)于echarts圖表無數(shù)據(jù)/空數(shù)據(jù)如何展示“暫無數(shù)據(jù)”的相關(guān)資料,需要的朋友可以參考下
    2022-10-10
  • JavaScript trim 實現(xiàn)去除字符串首尾指定字符的簡單方法

    JavaScript trim 實現(xiàn)去除字符串首尾指定字符的簡單方法

    下面小編就為大家?guī)硪黄狫avaScript trim 實現(xiàn)去除字符串首尾指定字符的簡單方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-12-12
  • 性能優(yōu)化篇之Webpack構(gòu)建速度優(yōu)化的建議

    性能優(yōu)化篇之Webpack構(gòu)建速度優(yōu)化的建議

    這篇文章主要介紹了性能優(yōu)化篇之Webpack構(gòu)建速度優(yōu)化的建議,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-04-04
  • 使用Webpack構(gòu)建多頁面程序的實現(xiàn)步驟

    使用Webpack構(gòu)建多頁面程序的實現(xiàn)步驟

    這篇文章主要介紹了使用Webpack構(gòu)建多頁面程序的實現(xiàn)步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-03-03
  • js實現(xiàn)俄羅斯方塊小游戲分享

    js實現(xiàn)俄羅斯方塊小游戲分享

    這篇文章主要介紹了js實現(xiàn)俄羅斯方塊小游戲分享,,需要的朋友可以參考下
    2014-01-01
  • JS發(fā)送請求的常用方法詳解

    JS發(fā)送請求的常用方法詳解

    在Web開發(fā)中,JavaScript與服務器進行數(shù)據(jù)交互是核心需求之一,本文將詳細解析JavaScript發(fā)送請求的常用方法,幫助開發(fā)者根據(jù)場景選擇最適合的方案,需要的朋友可以參考下
    2026-03-03
  • javascript使用輸出語句實現(xiàn)網(wǎng)頁特效代碼

    javascript使用輸出語句實現(xiàn)網(wǎng)頁特效代碼

    這篇文章主要介紹javascript使用輸出語句實現(xiàn)網(wǎng)頁特效,有需要的朋友可以參考下
    2015-08-08
  • 一文詳解JSON.parse和JSON.stringify的用法

    一文詳解JSON.parse和JSON.stringify的用法

    Json.stringify()和toString()兩者雖然都可以講目標值轉(zhuǎn)為字符串,但是還是有本質(zhì)區(qū)別的,下面這篇文章主要給大家介紹了關(guān)于JSON.parse和JSON.stringify用法的相關(guān)資料,需要的朋友可以參考下
    2023-01-01
  • js序列化和反序列化的使用講解

    js序列化和反序列化的使用講解

    今天小編就為大家分享一篇關(guān)于js序列化和反序列化的使用講解,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-01-01

最新評論

射洪县| 阜康市| 巧家县| 崇仁县| 沂水县| 伊宁县| 大埔县| 耒阳市| 邵武市| 夏河县| 阿荣旗| 秭归县| 宣汉县| 平泉县| 盐山县| 香河县| 襄城县| 巫溪县| 永仁县| 南木林县| 石屏县| 方山县| 景东| 江华| 安龙县| 正镶白旗| 应用必备| 崇文区| 佛学| 龙里县| 九江市| 通城县| 乐清市| 铜山县| 天祝| 商水县| 大埔区| 浪卡子县| 桐乡市| 嘉禾县| 咸丰县|