日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

vant组件实现上传图片裁剪_如何用 120 行代码,实现交互完整的拖拽上传组件?...

發(fā)布時間:2023/11/27 生活经验 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 vant组件实现上传图片裁剪_如何用 120 行代码,实现交互完整的拖拽上传组件?... 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

作者 | 前端勸退師

責編 | 伍杏玲

你將在該篇學到:

  • 如何將現(xiàn)有組件改寫為 React Hooks函數(shù)組件

  • useState、useEffect、useRef是如何替代原生命周期和Ref的。

  • 一個完整拖拽上傳行為覆蓋的四個事件:dragover、dragenter、drop、dragleave

  • 如何使用React Hooks編寫自己的UI組件庫。

逛國外社區(qū)時看到這篇:

How To Implement Drag and Drop for Files in React

文章講了React拖拽上傳的精簡實現(xiàn),但直接翻譯照搬顯然不是我的風格。

于是我又用React Hooks 重寫了一版,除CSS的代碼總數(shù) 120行。
效果如下:

添加基本目錄骨架

app.js

import?React?from?'react';import?PropTypes?from?'prop-types';import?{?FilesDragAndDrop?}?from?'../components/Common/FilesDragAndDropHook';export?default?class?App?extends?React.Component?{static?propTypes?=?{};
????onUpload?=?(files)?=>?{console.log(files);
????};
????render()?{return?(<div><FilesDragAndDroponUpload={this.onUpload}
????????????????/>div>
????????);
????}
}

FilesDragAndDrop.js(非Hooks):

import?React?from?'react';import?PropTypes?from?'prop-types';import?'../../scss/components/Common/FilesDragAndDrop.scss';export?default?class?FilesDragAndDrop?extends?React.Component?{static?propTypes?=?{onUpload:?PropTypes.func.isRequired,
????};
????render()?{return?(<div?className='FilesDragAndDrop__area'>
????????????????傳下文件試試?<spanrole='img'aria-label='emoji'className='area__icon'
????????????????>
????????????????????😎span>div>
????????);
????}
}

1. 如何改寫為 Hooks 組件?

請看動圖:

2. 改寫組件

Hooks版組件屬于函數(shù)組件,將以上改造:

import?React,?{?useEffect,?useState,?useRef?}?from?"react";import?PropTypes?from?'prop-types';import?classNames?from?'classnames';import?classList?from?'../../scss/components/Common/FilesDragAndDrop.scss';
const?FilesDragAndDrop?=?(props)?=>?{return?(
'FilesDragAndDrop__area'>
????????????傳下文件試試?????????????????role='img'
????????????????aria-label='emoji'
????????????????className='area__icon'
????????????>
????????????????&#128526;

????);
}
FilesDragAndDrop.propTypes?=?{
????onUpload:?PropTypes.func.isRequired,
????children:?PropTypes.node.isRequired,
????count:?PropTypes.number,
????formats:?PropTypes.arrayOf(PropTypes.string)
}export?{?FilesDragAndDrop?};
FilesDragAndDrop.scss
.FilesDragAndDrop?{
??.FilesDragAndDrop__area?{
????width:?300px;
????height:?200px;
????padding:?50px;
????display:?flex;
????align-items:?center;
????justify-content:?center;
????flex-flow:?column?nowrap;
????font-size:?24px;
????color:?#555555;
????border:?2px?#c3c3c3?dashed;
????border-radius:?12px;
????.area__icon?{
??????font-size:?64px;
??????margin-top:?20px;
????}
??}
}
然后就可以看到頁面:

實現(xiàn)分析

從操作DOM、組件復用、事件觸發(fā)、阻止默認行為、以及Hooks應用方面分析。

1. 操作DOM:`useRef`

由于需要拖拽文件上傳以及操作組件實例,需要用到ref屬性。

React Hooks中 新增了useRef API
語法

const?refContainer?=?useRef(initialValue);
  • useRef 返回一個可變的 ref 對象
  • 其 .current 屬性被初始化為傳遞的參數(shù)(initialValue)

  • 返回的對象將存留在整個組件的生命周期中。

...

const?drop?=?useRef();return?(<divref={drop}className='FilesDragAndDrop'
????/>
????...
????)

2. 事件觸發(fā)

完成具有動態(tài)交互的拖拽行為并不簡單,需要用到四個事件控制:

  • 區(qū)域外:dragleave,離開范圍

  • 區(qū)域內(nèi):dragenter,用來確定放置目標是否接受放置。

  • 區(qū)域內(nèi)移動:dragover,用來確定給用戶顯示怎樣的反饋信息

  • 完成拖拽(落下):drop,允許放置對象。

這四個事件并存,才能阻止 Web 瀏覽器默認行為和形成反饋。

3. 阻止默認行為

代碼很簡單:

e.preventDefault()?//阻止事件的默認行為(如在瀏覽器打開文件)
e.stopPropagation()?//?阻止事件冒泡
每個事件階段都需要阻止,為啥呢?舉個?栗子:
const?handleDragOver?=?(e)?=>?{//?e.preventDefault();//?e.stopPropagation();
};
不阻止的話,就會觸發(fā)打開文件的行為,這顯然不是我們想看到的。

4. 組件內(nèi)部狀態(tài): useState

拖拽上傳組件,除了基礎的拖拽狀態(tài)控制,還應有成功上傳文件或未通過驗證時的消息提醒。
狀態(tài)組成應為:

state?=?{
????dragging:?false,
????message:?{
????????show:?false,
????????text:?null,
????????type:?null,
????},
};
寫成對應useState前先回歸下寫法:
const?[屬性,?操作屬性的方法]?=?useState(默認值);
于是便成了:
const?[dragging,?setDragging]?=?useState(false);const?[message,?setMessage]?=?useState({?show:?false,?text:?null,?type:?null?});

5. 需要第二個疊加層

除了drop事件,另外三個事件都是動態(tài)變化的,而在拖動元素時,每隔 350 毫秒會觸發(fā) dragover事件。

此時就需要第二ref來統(tǒng)一控制。

所以全部的ref為:

const?drop?=?useRef();?//?落下層const?drag?=?useRef();?//?拖拽活動層

6. 文件類型、數(shù)量控制

我們在應用組件時,prop需要傳入類型和數(shù)量來控制

<FilesDragAndDroponUpload={this.onUpload}count={1}formats={['jpg',?'png']}
><div?className={classList['FilesDragAndDrop__area']}>
????????傳下文件試試?<spanrole='img'aria-label='emoji'className={classList['area__icon']}
????????>
????????????😎span>div>FilesDragAndDrop>
  • onUpload:拖拽完成處理事件
  • count: 數(shù)量控制

  • formats: 文件類型。

對應的組件Drop內(nèi)部事件:handleDrop:

const?handleDrop?=?(e)?=>?{
????e.preventDefault();
????e.stopPropagation();
????setDragging(false)const?{?count,?formats?}?=?props;const?files?=?[...e.dataTransfer.files];if?(count?&&?count?????????showMessage(`抱歉,每次最多只能上傳${count}?文件。`,?'error',?2000);return;
????}if?(formats?&&?files.some((file)?=>?!formats.some((format)?=>?file.name.toLowerCase().endsWith(format.toLowerCase()))))?{
????????showMessage(`只允許上傳?${formats.join(',?')}格式的文件`,?'error',?2000);return;
????}if?(files?&&?files.length)?{
????????showMessage('成功上傳!',?'success',?1000);
????????props.onUpload(files);
????}
};
.endsWith是判斷字符串結尾,如:"abcd".endsWith("cd"); ? // true

showMessage則是控制顯示文本:

const?showMessage?=?(text,?type,?timeout)?=>?{
????setMessage({?show:?true,?text,?type,?})
????setTimeout(()?=>
????????setMessage({?show:?false,?text:?null,?type:?null,?},),?timeout);
};
需要觸發(fā)定時器來回到初始狀態(tài)

7. 事件在生命周期里的觸發(fā)與銷毀

原本EventListener的事件需要在componentDidMount添加,在componentWillUnmount中銷毀:

componentDidMount?()?{this.drop.addEventListener('dragover',?this.handleDragOver);
}
componentWillUnmount?()?{this.drop.removeEventListener('dragover',?this.handleDragOver);
}
但Hooks中有內(nèi)部操作方法和對應useEffect來取代上述兩個生命周期

useEffect示例:

useEffect(()?=>?{document.title?=?`You?clicked?${count}?times`;
},?[count]);?//?僅在?count?更改時更新
而 每個effect都可以返回一個清除函數(shù)。如此可以將添加(componentDidMount)和移除(componentWillUnmount) 訂閱的邏輯放在一起。

于是上述就可以寫成:

useEffect(()?=>?{
????drop.current.addEventListener('dragover',?handleDragOver);return?()?=>?{
????????drop.current.removeEventListener('dragover',?handleDragOver);
????}
})
這也太香了吧!!!

完整代碼

FilesDragAndDropHook.js:

import?React,?{?useEffect,?useState,?useRef?}?from?"react";import?PropTypes?from?'prop-types';import?classNames?from?'classnames';import?classList?from?'../../scss/components/Common/FilesDragAndDrop.scss';const?FilesDragAndDrop?=?(props)?=>?{const?[dragging,?setDragging]?=?useState(false);const?[message,?setMessage]?=?useState({?show:?false,?text:?null,?type:?null?});const?drop?=?useRef();const?drag?=?useRef();
????useEffect(()?=>?{//?useRef?的?drop.current?取代了?ref?的?this.drop
????????drop.current.addEventListener('dragover',?handleDragOver);
????????drop.current.addEventListener('drop',?handleDrop);
????????drop.current.addEventListener('dragenter',?handleDragEnter);
????????drop.current.addEventListener('dragleave',?handleDragLeave);return?()?=>?{
????????????drop.current.removeEventListener('dragover',?handleDragOver);
????????????drop.current.removeEventListener('drop',?handleDrop);
????????????drop.current.removeEventListener('dragenter',?handleDragEnter);
????????????drop.current.removeEventListener('dragleave',?handleDragLeave);
????????}
????})const?handleDragOver?=?(e)?=>?{
????????e.preventDefault();
????????e.stopPropagation();
????};const?handleDrop?=?(e)?=>?{
????????e.preventDefault();
????????e.stopPropagation();
????????setDragging(false)const?{?count,?formats?}?=?props;const?files?=?[...e.dataTransfer.files];if?(count?&&?count?????????????showMessage(`抱歉,每次最多只能上傳${count}?文件。`,?'error',?2000);return;
????????}if?(formats?&&?files.some((file)?=>?!formats.some((format)?=>?file.name.toLowerCase().endsWith(format.toLowerCase()))))?{
????????????showMessage(`只允許上傳?${formats.join(',?')}格式的文件`,?'error',?2000);return;
????????}if?(files?&&?files.length)?{
????????????showMessage('成功上傳!',?'success',?1000);
????????????props.onUpload(files);
????????}
????};const?handleDragEnter?=?(e)?=>?{
????????e.preventDefault();
????????e.stopPropagation();
????????e.target?!==?drag.current?&&?setDragging(true)
????};const?handleDragLeave?=?(e)?=>?{
????????e.preventDefault();
????????e.stopPropagation();
????????e.target?===?drag.current?&&?setDragging(false)
????};const?showMessage?=?(text,?type,?timeout)?=>?{
????????setMessage({?show:?true,?text,?type,?})
????????setTimeout(()?=>
????????????setMessage({?show:?false,?text:?null,?type:?null,?},),?timeout);
????};return?(<divref={drop}className={classList['FilesDragAndDrop']}
????????>
????????????{message.show?&&?(<divclassName={classNames(classList['FilesDragAndDrop__placeholder'],classList[`FilesDragAndDrop__placeholder--${message.type}`],
????????????????????)}
????????????????>
????????????????????{message.text}<spanrole='img'aria-label='emoji'className={classList['area__icon']}
????????????????????>
????????????????????????{message.type?===?'error'???<>😢>?:?<>😘>}span>div>
????????????)}
????????????{dragging?&&?(<divref={drag}className={classList['FilesDragAndDrop__placeholder']}
????????????????>
????????????????????請放手<spanrole='img'aria-label='emoji'className={classList['area__icon']}
????????????????????>
????????????????????????😝span>div>
????????????)}
????????????{props.children}div>
????);
}
FilesDragAndDrop.propTypes?=?{onUpload:?PropTypes.func.isRequired,children:?PropTypes.node.isRequired,count:?PropTypes.number,formats:?PropTypes.arrayOf(PropTypes.string)
}export?{?FilesDragAndDrop?};
App.js:
import?React,?{?Component?}?from?'react';import?{?FilesDragAndDrop?}?from?'../components/Common/FilesDragAndDropHook';import?classList?from?'../scss/components/Common/FilesDragAndDrop.scss';export?default?class?App?extends?Component?{
????onUpload?=?(files)?=>?{console.log(files);
????};
????render?()?{return?(<FilesDragAndDroponUpload={this.onUpload}count={1}formats={['jpg',?'png',?'gif']}
????????????><div?className={classList['FilesDragAndDrop__area']}>
????????????????????傳下文件試試?<spanrole='img'aria-label='emoji'className={classList['area__icon']}
????????????????????>
????????????????????????😎span>div>FilesDragAndDrop>
????????)
????}
}
FilesDragAndDrop.scss:
.FilesDragAndDrop?{
??position:?relative;
??.FilesDragAndDrop__placeholder?{
????position:?absolute;
????top:?0;
????left:?0;
????right:?0;
????bottom:?0;
????width:?100%;
????height:?100%;
????z-index:?9999;
????display:?flex;
????align-items:?center;
????justify-content:?center;
????flex-flow:?column?nowrap;
????background-color:?#e7e7e7;
????border-radius:?12px;
????color:?#7f8e99;
????font-size:?24px;
????opacity:?1;
????text-align:?center;
????line-height:?1.4;
????&.FilesDragAndDrop__placeholder--error?{
??????background-color:?#f7e7e7;
??????color:?#cf8e99;
????}
????&.FilesDragAndDrop__placeholder--success?{
??????background-color:?#e7f7e7;
??????color:?#8ecf99;
????}
????.area__icon?{
??????font-size:?64px;
??????margin-top:?20px;
????}
??}
}
.FilesDragAndDrop__area?{
??width:?300px;
??height:?200px;
??padding:?50px;
??display:?flex;
??align-items:?center;
??justify-content:?center;
??flex-flow:?column?nowrap;
??font-size:?24px;
??color:?#555555;
??border:?2px?#c3c3c3?dashed;
??border-radius:?12px;
??.area__icon?{
????font-size:?64px;
????margin-top:?20px;
??}
}
然后你就可以拿到文件慢慢耍了。

?熱 文?推 薦?

?30 歲的程序員,我沒有活成理想的模樣,失敗嗎?

?華為方舟編譯器開源!前華為人重磅解讀!

?漫畫:關于 Context 不得不說的細節(jié)

?2019 編程語言排行榜:Java、Python 龍爭虎斗!PHP 屹立不倒!

?看懂“大數(shù)據(jù)”,這一篇就夠了!?只給測試集不給訓練集,要怎么做自己的物體檢測器??倒計時3天 | 專屬AI技術人的盛會,為你而來!?以太坊新生合約總數(shù)驟減; 比特幣大跌, 本周主鏈排名震蕩 | 數(shù)據(jù)周榜?她說:行!沒事別嫁程序員!
你點的每個“在看”,我都認真當成了喜歡

總結

以上是生活随笔為你收集整理的vant组件实现上传图片裁剪_如何用 120 行代码,实现交互完整的拖拽上传组件?...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。