بارگیری تصاویر با React/JavaScript – انجمن DEV 👩💻👨💻

در اولین مقاله از این مجموعه، من در مورد چالش های مربوط به گرفتن کدهای رنگ دیجیتال از اشیاء دنیای واقعی صحبت کردم. (یا همانطور که به این آموزش مربوط می شود، نمونه های دنیای واقعی از رنگ.) اما هنگامی که من یک نمایش دیجیتال معقول از هر رنگ موجودی خود داشتم، مجبور شدم در واقع تصاویری را که میخواستم دستکاری کنم، بگیرم.
در حال بارگذاری تصویر
من بحث کردم که آیا این مقاله را بنویسم زیرا آپلود فایل ها یک کار بسیار اساسی در جعبه ابزار یک توسعه دهنده است. اما بیشتر آموزش هایی که در مورد پردازش فایل ها می بینید، فرض می کنند که شما واقعاً اینطور هستید در حال آپلود تصویر – به برخی از سرورها.
اما در این مورد، ما در حال ساخت یک برنامه React هستیم و نیاز خاصی به ذخیره تصاویر در سرور نداریم. ما فقط باید آنها را بگیریم تا بتوان آنها را به صورت برنامه ای مدیریت کرد. بنابراین من می خواهم نشان دهم که چگونه این کار را در اینجا انجام می دهم.
این یک نسخه کاملاً حذف شده از مؤلفه UI است که من در Paint Map Studio از آن استفاده می کنم:
/* UI.js */
export const UIState = createContext({});
export const UI = props => {
const blob = useRef(null);
const file = useRef(null);
const [stats, setStats] = useState({});
return <>
<UIState.Provider value={{
blob,
file,
setStats,
stats,
}}>
<IndexContainer/>
<div>
<canvas id={'canvas'}></canvas>
</div>
</UIState.Provider>
</>;
};
کل برنامه تحت این مؤلفه UI زندگی می کند. در اینجا فقط به چند نکته اشاره می کنم:
-
من از زمینه برای تداوم متغیرها و اشتراکگذاری وضعیت استفاده میکنم. به همین دلیل داوری برای وجود دارد
blob
وfile
.stats
اطلاعات مربوط به تصویر پردازش شده را نگه می دارد. من میخواهم آن مقادیر در حافظه باقی بمانند تا کاربر مجبور نباشد دائماً همان فایل را هر بار که میخواهد تنظیمات را تغییر دهد دوباره بارگذاری کند. -
توجه داشته باشید که یک وجود دارد
<canvas>
عنصر درست در اینجا در بالای برنامه جاسازی شده است. آن عنصر در نهایت تصویر پردازش شده ما را نگه می دارد و نمایش می دهد.
حالا بیایید به مؤلفه IndexContainer نگاه کنیم:
/* IndexContainer.js */
export const IndexState = createContext({});
export const IndexContainer = () => {
return <>
<IndexState.Provider value={{}}>
<Index/>
</IndexState.Provider>
</>
}
به نظر می رسد یک جزء بسیار بی معنی است، درست است؟ خوب، در حال حاضر فقط یک مکان نگهدارنده است. IndexContainer Index را می پیچد زیرا در نهایت من از آن برای ذخیره همه متغیرهای فرم موجود در مؤلفه Index استفاده خواهم کرد. آنها از طریق زمینه قابل دسترسی خواهند بود. اما در حال حاضر، واقعاً هیچ کاری برای این مؤلفه وجود ندارد، به جز فراخوانی Index.
حالا بیایید به مؤلفه Index نگاه کنیم:
/* Index.js */
export const Index = () => {
const selectImageInputRef = useRef(null);
const file = useFile();
const handleFile = (event = {}) => {
const [source] = event.target.files;
file.read(source);
};
const handleImageButton = () => {
selectImageInputRef.current && selectImageInputRef.current.click();
}
return <>
<input
accept={'image/*'}
className={'displayNone'}
onChange={handleFile}
ref={selectImageInputRef}
type={inputType.file}
/>
<Button
onClick={handleImageButton}
variant={'contained'}
>
Select Image
</Button>
</>;
};
در اینجا ما یک پنهان داریم <input>
عنصری که در واقع کار گرفتن تصویر انتخابی کاربر را انجام می دهد. ما همچنین یک رابط کاربری Material UI داریم <Button>
که کنترل می کند <input>
عنصر به همین دلیل است که ما آن را داریم selectImageInputRef
مرجع.
این handleFile()
تابع چیزی است که در واقع پردازش فایل تصویر را شروع می کند. به طور خاص، آن را صدا می کند read()
عملکرد در useFile
قلاب.
پس بیایید نگاه کنیم useFile
قلاب:
/* useFile.js */
export const useFile = () => {
const image = useImage();
const uiState = useContext(UIState);
const read = (chosenFile = {}) => {
const fileReader = new FileReader();
fileReader.onloadend = event => {
uiState.file.current = chosenFile;
uiState.blob.current = event.target.result;
image.create(uiState.blob.current);
};
try {
fileReader.readAsDataURL(chosenFile);
} catch (e) {
// no file - do nothing
}
};
return {
read,
};
};
یک نیز وجود دارد reload()
عملکرد در نسخه نهایی از useFile
قلاب. اما در حال حاضر، ما فقط نیاز داریم read()
. توجه کنید که read()
مکان فایل و داده ها را در ref هایی که در آن ایجاد کردیم ذخیره می کند UI
جزء.
دارم یک نمونه جدید میسازم FileReader
و سپس با استفاده از readAsDataURL()
برای بارگذاری محتویات پس از بارگیری، onloadend
شلیک خواهد کرد، که در نهایت به create()
عملکرد در useImage
قلاب.
در اینجا یک نسخه بدون استخوان از آن است useImage
قلاب:
/* useImage.js */
export const useImage = () => {
const canvas = useRef(null);
const context = useRef(null);
const image = useRef(null);
useEffect(() => {
canvas.current = document.getElementById('canvas');
}, []);
const create = (src = '') => {
const source = src === '' ? image.current.src : src;
const newImage = new Image();
newImage.src = source;
newImage.onload = () => {
image.current = newImage;
canvas.current.width = newImage.width;
canvas.current.height = newImage.height;
context.current = canvas.current.getContext('2d', {alpha: false, willReadFrequently: true});
context.current.drawImage(newImage, 0, 0);
}
};
return {
create,
};
};
اینجا جایی است که جادوی رندر رخ می دهد. منبع از read()
عملکرد در useFile
قلاب. سپس از آن منبع برای ایجاد یک تصویر مجازی در حافظه استفاده می شود new Image()
.
هنگامی که تصویر مجازی بارگذاری شد، onload
آتش سوزی های رویداد در آن تابع من از canvas
ref که داخلش وصل بود useEffect
. هنگامی که یک زمینه بوم جدید ایجاد کردم، می توانم از آن استفاده کنم newImage
برای رندر کردن آن مقادیر در داخل متن.
این فایل تصویر انتخابی کاربر را می گیرد و روی صفحه نمایش می دهد. در این مرحله، تمام کاری که ما انجام داده ایم این است که تصویر خام را ارائه کنیم. اما این مرحله اولیه مهم است. زیرا اکنون که بوم را رندر کرده ایم (و در حافظه باقی مانده است)، می توانیم آن را برای نیازهای خود دستکاری کنیم.
در قسط بعدی…
اکنون که تصویر را بارگذاری کردیم، مرحله بعدی پیکسل کردن آن خواهد بود. در مقاله بعدی نحوه انجام این کار را به صورت برنامه ای نشان خواهم داد.