No processo de desenvolvimento front-end, muitas vezes encontramos a necessidade de upload de arquivo ou upload de imagem. Em alguns cenários, pode ser necessário cortar e compactar a imagem após o upload. Para programadores que não querem trabalhar horas extras, o o primeiro ponto é usar bibliotecas de terceiros. Embora bibliotecas como antd, element e outras bibliotecas com as quais estamos familiarizados forneçam componentes de upload:
No entanto, por um lado, essas bibliotecas de interface do usuário de terceiros são relativamente grandes e não leves o suficiente e, por outro lado, não suportam funções como corte e compactação, portanto, ainda precisam implementar ou integrar bibliotecas de terceiros Obviamente antd
, uma biblioteca antd-img-crop
para corte de imagem é fornecida. No entanto, é extremamente "desconfortável" (restrito) de usar, incapaz de cortar livremente a imagem e também incapaz de fornecer recursos de compactação:
Portanto, esta solução também é simples pass
. No final, para obter o efeito desejado, decidi me sacrificar e implementar manualmente um componente que suporta upload, recorte e compactação, e o nomeei react-cropper-pro
.
Introdução ao uso de react-cropper-pro
react-cropper-pro
É um componente simples e leve de upload + recorte + compactação de imagens, que não depende antd / element
de interfaces de usuário de terceiros e pode implementar rapidamente operações relacionadas ao processamento de imagens, com dependências subjacentes react-cropper
.
Instalar
yarn add react-cropper-pro
usar
import CropperPro from 'react-cropper-pro';
export default () =>
<CropperPro
defaultImg=""
onChange={(file) => console.log(file)}
onDel={(image) => console.log('remove', image)}
/>;
introdução da API
Realização técnica
Tecnicamente, existem os seguintes pontos principais:
- Implemente o estilo do componente de upload de arquivo (principalmente para substituir a "IU pura" do input[arquivo] nativo)
- Realize o corte repentino da imagem
- Implementar compactação de imagem
- Empacotado como um componente de reação e publicado no npm
A seguir, apresentaremos brevemente os detalhes da implementação.
1. Implemente o estilo do componente de upload de arquivo
Todos nós sabemos que o estilo padrão usado pelo componente de upload do arquivo de entrada do elemento é muito simples, então precisamos substituir o estilo nativo de alguma forma.Aqui vou compartilhar com vocês como eu o implementei.
Na verdade, é muito simples, basta usar o método de posicionamento para cobrir um div do mesmo tamanho na entrada e, em seguida, deixar o evento div penetrar e responder ao evento de entrada. O código principal é o seguinte:
<div className="xi-cropper-upload">
<input type="file" onChange={handleChange} accept="image/gif,image/jpeg,image/jpg,image/png" />
<div className="xi-cropper-file">
</div>
</div>
A segunda é que após o usuário fazer o upload da imagem, a imagem pode ser exibida na área de upload, da seguinte forma:
O código completo para este bloco é o seguinte:
<div className="xi-cropper-upload">
<input type="file" onChange={handleChange} accept="image/gif,image/jpeg,image/jpg,image/png" />
<div className="xi-cropper-file">
{
cropData ? <img src={cropData} /> : '+'
}
{
!!cropData && <span className="xi-cropper-del" onClick={handleDel}>✕</span>
}
</div>
</div>
2. Realize o corte repentino da imagem
图片裁切这里我采用了 react-cropper
这个库, 虽然不能直接实现图片上传, 但是它的图片裁切能力还是很强大的. 我们需要实现的效果是图片上传后会出现上传弹窗, 显示裁切区域, 如下:
这里弹窗的实现我采用了React-Dom的createPortal
API, 它可以实现弹窗dom挂载在指定的容器上, 很多组件库的组件比如抽屉, Modal, DropDown都采用了类似的实现原理, 我之前也写了一篇文章来介绍如何使用 createPortal
的, 感兴趣的朋友可以学习参考一下:
《精通react/vue组件设计》之配合React Portals实现一个功能强大的抽屉(Drawer)组件
这块的核心源码可以参考github仓库对应的第134
行.
文件路径如下: github.com/MrXujiang/r…
3. 实现图片压缩
图片压缩这块主要是基于产品业务场景来设计, 这里我分了4个档:
- 不压缩
- 高
- 中
- 低
如下:
这一块主要是利用了canvas
和 cropper
的能力, 我们通过控制canvas对象的宽高比例, 和canvas
提供的toBlob
来实现图片的压缩, 核心代码如下:
if (typeof cropper !== "undefined") {
setCropData(cropper.getCroppedCanvas().toDataURL());
const rate = 1 / (4 - imgLevel);
const { width, height } = cropper.getCropBoxData();
cropper.getCroppedCanvas({
width: width * rate,
height: height * rate,
imageSmoothingQuality: imgLevelValueMap[imgLevel],
fillColor: 'transparent',
}).toBlob((blob: Blob) => {
if(blob) {
const time = Date.now();
let croppedFile:any = new File([blob], fileRef.current.name, {
type: fileRef.current.type,
lastModified: Date.now(),
});
croppedFile.uid = time;
onChange && onChange(croppedFile)
}else {
new Error('图片裁切失败');
}
}, fileRef.current.type, rate)
}
当然有关 canvas
的toBlob
API不熟悉的朋友可以移步MDN:
developer.mozilla.org/zh-CN/docs/…
4. 包装成react组件并发布到npm
有关如何实现组件库以及如何优雅发布到NPM公仓的技术我之前在《趣谈前端》 也分享过, 感兴趣的朋友可以参考一下: 从零教你搭建组件系统《高级进阶必备》
应用场景
Por fim, vamos dar uma olhada no cenário de aplicação, que react-cropper-pro
foi aplicado à H5-Dooring
plataforma de construção visual para obter um controle mais preciso das imagens:
Amigos interessados podem experimentá-lo. Claro, existem muitas plataformas de conteúdo. Devido à capacidade de escrita de editores como rich text ou md, também envolverá o controle de imagens, portanto, essas são react-cropper-pro
as áreas de aplicação do .
afinal
O código aberto não é fácil, bem-vindo para construir e construir juntos, clique em uma estrela para tornar a vida melhor.