问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

React 实现图片裁剪功能

创作时间:
作者:
@小白创作中心

React 实现图片裁剪功能

引用
CSDN
1.
https://blog.csdn.net/qq_40200207/article/details/138076944

本文将详细介绍如何在React中实现图片裁剪功能,重点讲解Canvas渲染图片时出现模糊问题的原因及解决方案。通过具体代码示例和详细解释,帮助读者掌握这一实用技术。

技术栈

本文涉及的技术栈包括:

  1. 前端框架:React
  2. 剪切组件:react-image-crop(可通过npm或yarn安装)
  3. UI框架:MUI

实现代码

下面是具体的实现代码:

import { Paper } from '@mui/material'
import React, { useRef, useState } from 'react'
import './test.css'
import ReactCrop from 'react-image-crop'
import 'react-image-crop/dist/ReactCrop.css'

function Test() {
    // 设置剪切框的初始数据
    const [crop, setCrop] = useState({unit: 'px',x:0,y:0,width:200,height:200})
    // 获取canvas的真实DOM
    const canvasRef = useRef()
    // 获取img的真实DOM
    const imgRef = useRef()
    const [url,setUrl] = useState(null)

    // 图片初次加载时的回调函数
    const onImageLoad = () =>{
        const canvas = canvasRef.current
        const image = imgRef.current
        image.setAttribute('crossOrigin', 'anonymous')
        canvas.style.width = '200px';
        canvas.style.height = '200px';
        canvas.width = 200 * devicePixelRatio
        canvas.height = 200 *devicePixelRatio
        const context = canvas.getContext("2d")
        let width = (200 / image.width) * image.naturalWidth
        let height = (200 / image.height) * image.naturalHeight
        context.drawImage(image, 0,0,width,height, 0,0,canvas.width,canvas.height)
    }

    // Crop位置发生位移时的回调函数
    const onCropChange = (c) =>{
        setCrop(c)
        const canvas = canvasRef.current
        const image = imgRef.current
        canvas.style.width = '200px';
        canvas.style.height = '200px';
        canvas.width = image.width * devicePixelRatio
        canvas.height = image.height * devicePixelRatio
        const context = canvas.getContext("2d")
        const width = c.width * (image.naturalWidth / image.width);
        const height = c.height * (image.naturalHeight / image.height);
        let x = c.x * (image.naturalWidth / image.width)
        let y = c.y * (image.naturalHeight / image.height)
        context.drawImage(image, x,y,width,height, 0,0,canvas.width,canvas.height)
    }

    return (
        <div>
            <Paper className='paper'>
                <ReactCrop crop={crop} onChange={onCropChange} className='cropper'>
                    <img ref={imgRef} src= "https://cdn.pixabay.com/photo/2014/05/13/16/19/porto-343487_1280.jpg" onLoad={onImageLoad}/>
                </ReactCrop>
                <div>
                    <canvas ref={canvasRef} ></canvas>
                </div>  
            </Paper>
        </div>
    )
}

export default Test  

下面是对应的CSS代码:

.paper {
    width: 600px;
    height: 500px;
    outline: none;
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%,-50%);
    .cropper {
        width: 300px;
        object-fit: cover;
        margin-bottom: 5rem;
    }
}

在Test组件中定义了一个图片剪辑区和预览区。可以看到,上面是剪切图片的区域,下面是用Canvas渲染的区域。

基本概念

像素

像素是数字图像的最基本单位。生活中谈到一个图片有多大,我们会用用 XX px * XX px来表示。

我们可以看到下面这张像素图(像素图是一种绘画风格),是由一个个有颜色的方格子组成,每个格子就是1个像素

这两张图都是由1个个像素组成的。

物理像素

物理像素就是我们电脑、手机的分辨率,比如电脑屏幕分辨率是1920*1080 ,可以理解为横向有1920个格子,纵向有1080个格子,总共有207.36万个格子,每个格子就是一个像素点。

逻辑像素


逻辑分辨率就是前端CSS里的px了,逻辑像素并没有固定的物理长度。不能说1px的长度是几毫米,还是几厘米。这只是一个逻辑概念。

逻辑像素和物理像素之间的关系

上图表示有一个屏幕,物理分辨率为 5 * 6 ,屏幕中有一个Logo 图片。图片分辨率为3 * 4

现在我们换一个分辨率为 10 * 12 的屏幕,发现它变小了,这是为什么呢?所谓的逻辑分辨率为3*4是指在屏幕上占用 3 * 4个 物理像素点,由于每个物理像素点变小,那么显示的图片也会随之变小。

DPR

为了使得同一个逻辑像素能在不同分辨率的设备上保持一样的效果,设备制造商指定了一个设备像素比(dpr)。意思就是1px 的逻辑像素对应多少的物理像素。

原来在分辨率5 * 6 设备上展示的图片在 10* 12 设备上变小的原因是单个像素点变小了,这个时候,只要使得1个px的逻辑像素对应的物理像素由1 变成 2 ,也就是DPR为2 ,这样就能完美解决这一问题了。

位图像素

位图(Bitmap)我们常见的很多图片都是位图。比如这张。位图的基本组成单位是像素,我们就把这些像素叫作位图像素。这张图片就是由千千万万个位图像素组成的,一个位图像素中包含了很多的二进制数据

我们看一下上面这张图的信息,分辨率1000* 420 指的是宽度上由1000个位图像素组成,高度上由420个位图像素组成,宽度指的是宽度上可以占1000个逻辑像素,高度指的是高度上可以占420个逻辑像素。位深度指的是单个元素能显示多少种颜色,这张图片是32位,可以显示2的32次方种颜色

位图像素和逻辑像素

位图像素可以理解为就是逻辑像素,位图像素是专门用来描述位图的,1px 位图像素和1px 逻辑像素并没有什么差别,但是要注意,图片的位图像素和页面的展示的宽高是不同的概念。就拿上个图片来说,位图像素是1000 *420 ,说明图片可以在页面宽度上占1000个逻辑像素,但实际的展示宽度却并不一定是1000px,

请看这里的css宽高,是640 *324.6 这是个响应式页面,页面缩小,元素也跟着缩小

随着页面的缩小,css 的尺寸也在缩小。可见图片自身的分辨率和在页面的展示宽高完全是两个不同的概念。

开始分析canvas绘制图片模糊的原因

前面说过DPR 能够解决逻辑像素在不同分辨率的设备上以相同效果显示的问题,但我们接下来遇到的问题也是由它造成的。

请看下图,左图是标准屏幕,右图是高清屏幕

当DPR 为1 时,1个位图(Bitmap) 像素 = 1个物理像素,此时图像显示出来是清晰的。

当DPR 为2时, 1个位图像素(Bitmap) = 2 * 2 个物理像素,此时图像显示出来是模糊的。这是为什么呢?因为在位图中像素已经是最小的单位了,不可以拿一个位图像素的去填充4个物理像素,剩余的3个位图像素会使用类似的颜色填充

为什么不使用原色填充呢?,这是因为在高清屏幕中使用原色填充,图案锯齿感非常明显,图像明显缺乏了一丝顺化。

那么怎么解决呢?这这个例子中,要想让图像显示我们就要想办法让 1个 位图像素 = 1 个物理像素

有一个好的办法,就是放大图片,那么放大多少呢?

在这个例子中, 我们要把宽度,和高度都放大2倍

1px * 2 * 1px * 2 = 4px 这样 左侧的1个逻辑像素就等于右侧的1个物理像素

在实际的编码中我们不可以用常量来进行放大,而要使用devicePixelRatio (缩写DPR)进行动态的放大,这里在Edge 控制台里面打印了当前的devicePixelRatio。

最终解决方案

这里把canvas 画布的width, height 都放大DPR倍。这时会有小伙伴有疑问了,你把原来的图片放大了,图片不就是我想要的尺寸了么? 没关系,我们可用以设置canvas.style.width和canvas.style.height ,这样就会canvas 展示在页面上的尺寸就是canvas.style.width和canvas.style.height 了,具体实现可以看实现代码

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号