国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

手寫一個 React 圖片預覽組件

這篇具有很好參考價值的文章主要介紹了手寫一個 React 圖片預覽組件。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

原文鏈接: 手寫一個 React 圖片預覽組件

前幾天打算給博客添加一個圖片預覽的效果,可在網(wǎng)上找了半天也沒找到合適的庫,于是自己干脆自己手寫了個。

最終實現(xiàn)效果如下:

手寫一個 React 圖片預覽組件

實現(xiàn)原理

當鼠標點擊圖片時生成一個半透明遮罩,并添加一個與點擊圖片位置大小都相同的圖片,之后通過 CSS 實現(xiàn)圖片的放大和居中,當再次點擊時,通過刪除樣式實現(xiàn)圖片的返回。

具體操作

添加遮罩和圖片

此處需要用到 ReactDom 的 createPortal() 方法,它可以將元素渲染到網(wǎng)頁中的指定位置。因為要考慮到圖片的返回,所以圖片的位置不能用 getBoundingClientRect() 提供的相對于視圖窗口的坐標,而是要用到 offsetTopoffsetLeft 提供的相對于 offsetParent 的坐標,所以需要將遮罩和圖片渲染到 body 元素中,并且二者需要為同一級。具體實現(xiàn)代碼如下:

import { createPortal } from 'react-dom';
import { useState, useRef } from 'react';

function Mask({ props, setStatus, imgRef }) {
    const close = () => {
        setStatus(false);
    };
    return createPortal(
        <div onClick={close} className='cursor-zoom-out'>
            <div className='fixed bottom-0 left-0 right-0 top-0 bg-black/75'></div>
            <img
                {...props}
                className='absolute'
                style={{
                    top: imgRef.current.offsetTop,
                    left: imgRef.current.offsetLeft,
                    width: imgRef.current.offsetWidth,
                    height: imgRef.current.offsetHeight,
                }}
            />
        </div>,
        document.body
    );
}

export default function Img(props) {
    const [status, setStatus] = useState(false);
    const imgRef = useRef(null);
    return (
        <>
            <img
                {...props}
                ref={imgRef}
                className={`cursor-zoom-in ${status ? 'invisible' : ''}`}
                onClick={() => {
                    setStatus(true);
                }}
                loading='lazy'
            />
            {status && <Mask props={props} setStatus={setStatus} imgRef={imgRef} />}
        </>
    );
}

此時點擊圖片便會在 body 下生成一個遮罩和處在相同位置的圖片,再次點擊時則會關閉。

手寫一個 React 圖片預覽組件

添加動畫效果

動畫效果主要由 CSS 中的 transitiontransform 實現(xiàn),而 transform 主要用到了其中的 scale()translate 函數(shù)。

scale() 的數(shù)值為圖片縮放的倍數(shù),我們需要將圖片盡量縮放到原先尺寸,但不能超出屏幕。所以要分別求出圖片寬度和高度的最大縮放倍數(shù),之后對比取最小值,但在計算圖片目標尺寸時,需要與屏幕尺寸對比取最小值。

const scaleX = Math.min(naturalWidth, viewportWidth) / width;
const scaleY = Math.min(naturalHeight, viewportHeight) / height;
const scale = Math.min(scaleX, scaleY);

translate() 的數(shù)值為圖片在 X 和 Y 軸上的偏移量,我們需要將圖片偏移到屏幕中心,所以要求出圖片中心點距屏幕中心點的橫縱距離

手寫一個 React 圖片預覽組件

const translateX = ((viewportWidth - width) / 2 - left) / scale;
const translateY = ((viewportHeight - height) / 2 - top) / scale;

具體計算函數(shù)如下

const calcFitScale = imgRef => {
    const { top, left, width, height } = imgRef.current.getBoundingClientRect();
    const { naturalWidth, naturalHeight } = imgRef.current;
    const viewportWidth = document.documentElement.clientWidth;
    const viewportHeight = document.documentElement.clientHeight;
    const scaleX = Math.min(Math.max(width, naturalWidth), viewportWidth) / width;
    const scaleY = Math.min(Math.max(height, naturalHeight), viewportHeight) / height;
    const scale = Math.min(scaleX, scaleY);
    const translateX = ((viewportWidth - width) / 2 - left) / scale;
    const translateY = ((viewportHeight - height) / 2 - top) / scale;
    return `scale(${scale}) translate(${translateX}px, ${translateY}px)`;
};

這里講一下為什么要在生成偏移量的時候除以縮放倍數(shù),因為 CSS 中 transform 的執(zhí)行是有先后順序的,圖片進行 scale() 縮放后其 translate() 的偏移距離也會發(fā)生變化,所以需要在計算時提前考慮。倘若要先進行偏移后進行縮放,則可以不考慮此因素。

const translateX = (viewportWidth - width) / 2 - left;
const translateY = (viewportHeight - height) / 2 - top;
return `translate(${translateX}px, ${translateY}px) scale(${scale})`;

最終代碼

最后加上一點滾動監(jiān)聽,屏幕監(jiān)聽,遮罩透明度變化即可得到最終函數(shù)

import { createPortal } from 'react-dom';
import { useState, useRef, useEffect } from 'react';

function Mask({ props, setStatus, imgRef }) {
    const [transform, setTransform] = useState('');
    const [opacity, setOpacity] = useState(0.7);
    const close = () => {
        setOpacity(0);
        setTransform('');
        setTimeout(() => {
            setStatus(false);
        }, 300);
    };
    useEffect(() => {
        const handleResize = () => {
            setTransform(calcFitScale(imgRef));
        };
        window.addEventListener('resize', handleResize);
        handleResize();
        return () => window.removeEventListener('resize', handleResize);
    }, []);
    useEffect(() => {
        window.addEventListener('scroll', close);
        return () => window.removeEventListener('scroll', close);
    }, []);
    return createPortal(
        <div onClick={close} className="cursor-zoom-out">
            <div
                className="fixed bottom-0 left-0 right-0 top-0 bg-black"
                style={{
                    opacity,
                    transition: 'opacity 300ms cubic-bezier(0.4, 0, 0.2, 1)',
                }}
            ></div>
            <img
                {...props}
                className="absolute"
                style={{
                    transition: 'transform 300ms cubic-bezier(.2, 0, .2, 1)',
                    top: imgRef.current.offsetTop,
                    left: imgRef.current.offsetLeft,
                    width: imgRef.current.offsetWidth,
                    height: imgRef.current.offsetHeight,
                    transform: transform,
                }}
            />
        </div>,
        document.body
    );
}

export default function Img(props) {
    const [status, setStatus] = useState(false);
    const imgRef = useRef(null);
    return (
        <>
            <img
                {...props}
                ref={imgRef}
                className={`cursor-zoom-in ${status ? 'invisible' : ''}`}
                onClick={() => {
                    setStatus(true);
                }}
                loading="lazy"
            />
            {status && <Mask props={props} setStatus={setStatus} imgRef={imgRef} />}
        </>
    );
}

/**
 * 計算圖片縮放比例
 */
const calcFitScale = imgRef => {
    const margin = 5;
    const { top, left, width, height } = imgRef.current.getBoundingClientRect();
    const { naturalWidth, naturalHeight } = imgRef.current;
    const viewportWidth = document.documentElement.clientWidth;
    const viewportHeight = document.documentElement.clientHeight;
    const scaleX = Math.min(Math.max(width, naturalWidth), viewportWidth) / width;
    const scaleY = Math.min(Math.max(height, naturalHeight), viewportHeight) / height;
    const scale = Math.min(scaleX, scaleY) - margin / Math.min(width, height) + 0.002;
    const translateX = ((viewportWidth - width) / 2 - left) / scale;
    const translateY = ((viewportHeight - height) / 2 - top) / scale;
    return `scale(${scale}) translate3d(${translateX}px, ${translateY}px, 0)`;
};

transform 的初始值并沒有直接從 calcFitScale() 中獲取,而是通過在 useEffect() 進行賦值,因為如果一開始就給圖片定義了 transform ,則不會產(chǎn)生動畫效果。

參考鏈接

Understanding translate after scale in CSS transforms

Why does order of transforms matter? rotate/scale doesn't give the same result as scale/rotate文章來源地址http://www.zghlxwxcb.cn/news/detail-480278.html

到了這里,關于手寫一個 React 圖片預覽組件的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內(nèi)容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • 圖片鏈接或pdf鏈接通過瀏覽器打開時,有時可以直接預覽,有時卻是下載,為什么?

    圖片鏈接或pdf鏈接通過瀏覽器打開時,有時可以直接預覽,有時卻是下載,為什么?

    在前端開發(fā)中,有時候需要對一些文件鏈接進行特殊處理,比如對于一些圖片鏈接或者PDF鏈接,有時我們需要通過瀏覽器打開進行預覽,有時又不希望通過瀏覽器進行打開,而是希望能夠直接下載到本地。但現(xiàn)實效果卻往往跟我們相反,我們希望瀏覽器打開時,他卻直接下載

    2024年02月10日
    瀏覽(26)
  • 解決:js 根據(jù)圖片鏈接(image url)下載,有的打開預覽,有的下載

    1、問題描述 https://*****/drugTestReport/20230515/202305151106111386737.png https://*****/drugTestReport/20230605/202306051540314553141.jpg 同樣結構的兩個圖片鏈接,使用window.open(url),一個是打開預覽,另一個是下載 ? 2、解決方法,通過fetch請求url,獲取blob類型,區(qū)分情況,統(tǒng)一成下載。 ?

    2024年02月09日
    瀏覽(21)
  • marked在vue項目中改變超鏈接跳轉方式和圖片放大預覽

    這里我是另起一個js文件對marked的配置做了修改,參考如下 然后在vue文件中進行進行該文件的引用 最后格式化markdown文本

    2024年02月11日
    瀏覽(18)
  • React antd upload組件上傳視頻并實現(xiàn)視頻預覽

    記錄問題:antd的upload組件文檔中對于視頻的上傳預覽沒有明確的文檔demo,在這里記錄一下 項目需求:支持圖片及視頻的上傳并實現(xiàn)預覽,點擊上傳后不會立即請求接口上傳資源,后續(xù)點擊確定再上傳 上代碼

    2024年02月04日
    瀏覽(30)
  • uniAPP 視頻圖片預覽組件

    uniAPP 視頻圖片預覽組件

    效果圖 ? 思路:處理文件列表,根據(jù)文件類型歸類 已兼容 H5? ios?設備,測試已通過 浙政釘,微信小程序 視頻資源因為,沒有預覽圖,用灰色圖層加播放按鈕代替

    2024年02月15日
    瀏覽(25)
  • vue實現(xiàn)預覽圖片及視頻組件

    vue實現(xiàn)預覽圖片及視頻組件

    ? ?組件代碼內(nèi)容?MediaViewer.vue 引用??VideoOrImagePreview.vue 使用 注:element-plus,vue3

    2024年02月04日
    瀏覽(23)
  • 強大的圖片預覽組件Viewer.js

    強大的圖片預覽組件Viewer.js

    ? Viewer.js 是一款強大的圖片查看器。我們通過Viewer.js 在頁面上添加強大的圖片查看功能,同時,這款優(yōu)秀的插件配置操作起來也非常的方便。 Viewer.js分為2個版本,js版本和jquery版本,下載地址分別為 純JS版本:https://github.com/fengyuanchen/viewerjs jQuery 版本:GitHub - fengyuanchen/j

    2024年01月18日
    瀏覽(40)
  • vue2自定義封裝圖片預覽組件

    前言:預覽圖片現(xiàn)在已經(jīng)有成熟的組件了,比如element ui的圖片預覽功能,但是現(xiàn)實開發(fā)過程中,element ui圖片預覽已經(jīng)不滿足需求了,比如涉及預覽時刪除圖片以及下載圖片 自定義封裝圖片預覽組件 功能:滾輪滾動圖片放大、還原圖片、左旋轉、右旋轉、上一張、下一張、刪

    2024年01月18日
    瀏覽(21)
  • el-upload 組件上傳/移除/報錯/預覽文件,預覽圖片、pdf 等功能

    el-upload 組件上傳/移除/報錯/預覽文件,預覽圖片、pdf 等功能

    dialog.vue @/styles/dialog-style.scss

    2024年02月06日
    瀏覽(100)
  • Vue3.0實現(xiàn)圖片預覽組件(媒體查看器)

    前言: 最近項目中有個場景,一組圖片、視頻、音頻、文件數(shù)據(jù),要求點擊圖片可以放大預覽,左右可以切換音視頻、文件,支持鼠標及各種鍵控制 縮放,左右旋轉,移動等功能,整理了一下,封了個組件,注釋很全面,每塊地方都有講解,可以直接拿到項目中使用 先看下

    2023年04月09日
    瀏覽(44)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包