I need help in fixing the code to prevent flashing of Iphone image. I have implemented various things like useMemo and useLayout and debounce and requestAnimationFrame but I am still facing issue
// i have imported useMemo, useRef, useCallback from React
import React, { useState, useMemo, useRef, useCallback } from "react";
import "./Iphone.css";
// I have imported debounce from lodash
import debounce from "lodash.debounce";
// I have created functional component called IPhone
const Iphone = () => {
// I have used selectedImage and OldImage variable here and initialized them to `null`
// I have used useMemo on initial loading of page to prevent flashing
const image = useMemo(() => new Image(), []);
// I have used useCallback function on initial loading of the page to handleImageChange function
// to change a image I called handleImageChange function
const handleImageChange = useCallback((event) => {
event.preventDefault();
const newImageURL = URL.createObjectURL(event.target.files[0]);
// i have passed newIMageURL to both SetSelectedImage and SetOldImage
setSelectedImage(newImageURL);
setOldImage(newImageURL);
}, []);
// I have called the function handleText to toggle showing of text
const handleText = () => {
setShowText(!showText);
};
// I have called useMemo function to prevent flashing as i drag the text to
various
locations
const debouncedShowContent = useMemo(
() => debounce((value) => setContent(value), 300),
[]
);
// i have called useCallback function to prevent flashing in handleDragStart
function
const handleDragStart = useCallback(() => {
// here I am setting dragging to true as i am starting to drag the text
setDragging(true);
}, []);
// I have called useCallback function to prevent flashing in handleDrag function
const handleDrag = useCallback(
(e) => {
// i am checking if dragging is true
if (dragging) {
// i am calling requestAnimation Frame here
requestAnimationFrame(() => {
// I am getting value of canvas via canvasRef.current
const canvas = canvasRef.current;
// i am saving value in ctx
const ctx = canvas.getContext("2d");
// i am getting dimensions using canvas.getBoundingClientRect and saving it
in rect
const rect = canvas.getBoundingClientRect();
// i am getting value of e.clientX - rect.left and saving it in x variable
const x = e.clientX - rect.left;
// i am getting value of e.clientY - rect.top and saving it in y variable
const y = e.clientY - rect.top;
// clearing the values of ctx via ctx.clearRect
ctx.clearRect(0, 0, canvas.width, canvas.height);
// i am inserting value into image the value oldImage
image.src = oldImage;
// on loading of image i am setting value in canvas
image.onload = () => {
// drawing image using ctx.drawImage
ctx.drawImage(image, 0, 0, canvas.width, canvas.height);
// i am using ctx.fillText to fill the text with value of content and x and
y
ctx.fillText(content, x, y);
I am setting position of x and y via setPosition
setPosition({ x, y });
// converting the value of image to text
const dataURI = canvas.toDataURL("image/png");
// saving in setSelectedImage the value of dataURI
setSelectedImage(dataURI);
};
});
}
},
[dragging, oldImage, content]
);
// i have called useCallback function to prevent flashing in handleDragEnd
function
const handleDragEnd = useCallback(() => {
// i am setting the position in x and y via position.x and position.y
setPosition({ x: position.x, y: position.y });
// i am setting dragging to false
setDragging(false);
}, [position]);
// i have called useCallback function to prevent flashing in addText function
const addText = useCallback(() => {
// here i am setting the image to be true
setShowImage(true);
// if image is not selected then do return;
if (!selectedImage || !content) return;
textDrawn.current = true;
requestAnimationFrame(() => {
// I am getting value of canvas via canvasRef.current
const canvas = canvasRef.current;
// i am saving value in ctx
const ctx = canvas.getContext("2d");
// i am inserting value into image the value selectedImage
image.src = selectedImage;
// on loading of image i am setting value in canvas
image.onload = () => {
// getting width of canvas from image.width
canvas.width = image.width;
// getting height of canvas from image.height
canvas.height = image.height;
// clearing the values of ctx via ctx.clearRect
ctx.clearRect(0, 0, canvas.width, canvas.height);
// drawing image using ctx.drawImage
ctx.drawImage(image, 0, 0);
// i am using ctx.fillText to fill the text with value of content
ctx.fillText(content, textPosition.x, textPosition.y);
// converting the value of image to text
const dataURI = canvas.toDataURL("image/png");
// saving in setSelectedImage the value of dataURI
setSelectedImage(dataURI);
};
});
}, [selectedImage, content, textPosition]);
// i have used debouncedShowContent function to prevent flashing in showContent
function
const showContent = (event) => {
// calling debouncedShowContent
debouncedShowContent(event.target.value);
};
// I am using handleSave function to save the image along with text in new
window
const handleSave = () => {
// if image is selected then I am opening new window
if (selectedImage) {
// opening a new window
const newWindow = window.open();
// writing a new image
newWindow.document.write(
`<img src="${selectedImage}" alt="resultingImage" />`
);
}
};
// i want to add more details. if you have any questions ask and i will clarify
quickly.
};
// I am exporting the Iphone function
export default Iphone;
I want to prevent flashing of image as I drag the text from one location to another. how to do that?
I can add more details. if you have any questions ask and I will clarify quickly.
I have posted comments in each line of the code. Is there anything else I should do to improve the question?
I'm going to focus on the
handleDragfunction ...Removing all the comments from your code, this is what we get:
First thing that stands out is the constants
canvas,ctxandrectyou should move those to global constants no need to instantiate those every time we call the drag function, or any other functions related to drawingSecond you have
image.onloadin this function, I don't think thatoldImageis changing value every time, better to do that once outside this function an make the image a global constantBelow are two samples where we can see the difference...
Loading the image once outside the drawing functions, (no flickering)
Loading the image in the drawing function, (some flickering expected)