By utilizing the code below, you can easily upload files through a drag and drop method and display them on a p5 canvas for further customization in each frame, whether it's an image or video frame.
DRAG AND DROP:
To learn more about drag and drop functionality, refer to the links provided in the p5 Reference page:
dragOver
-
dragLeave
-
drop
-
It is recommended to implement these functionalities within the preload()
function as it runs only once in p5js by default!
LOGIC OF THE PROVIDED CODE:
Whenever an item or multiple items are dropped onto the designated drag and drop area, the readFile()
function will process each item individually. You have the freedom to add custom logic, such as restricting file formats to specific types like png
and mp4
.
Moreover, every uploaded item has a click event listener attached so that clicking on the item stores the image source in a variable named uploadedItemSrc
and identifies if it's a video or an image.
DRAWING ON CANVAS:
The draw()
function allows you to adjust the frameRate. In p5.js, the maximum frame rate is determined by the device's performance. By default, p5.js aims to render frames as quickly as possible based on hardware and browser capabilities.
This feature enables you to manipulate video processing speed when needed. Additionally, before or after displaying the image or video frame, you can incorporate your preferred logic.
FINAL REMARKS:
Your statement:
"However, I want to be able to access all frames and loop through them as fast as possible (without waiting for the next frame to load in the video element)."
In p5JS, the drawFunction acts like a loop. The draw() function continues indefinitely unless frameRate = 0
or noLoop()
is called.
Creating a Play Button using standard HTML would be simpler. Nevertheless, this example demonstrates how you can achieve the same result using p5 shapes like rect and text.
const FPS = 120;
var dropzone
var elem;
let uploadedItemSrc;
var isImage = false;
var isVideo = false;
const hightlight = () => {
dropzone.style('background-color', '#4caf50')
};
const unhightlight = () => {
dropzone.style('background-color', '#fff')
};
//Play Button Info
let isPlaying = false;
let buttonWidth = 60;
let buttonHeight = 30;
let buttonX = 10;
let buttonY;
//Preload Method allow drag and drop
function preload() {
dropzone = select('#dropzone');
dropzone.dragOver(hightlight);
dropzone.dragLeave(unhightlight);
dropzone.drop(readFile, unhightlight);
}
//Create Canvas and set up play button positioning
function setup() {
var canvasParent = document.getElementById('canvasParent');
var myCanvas = createCanvas(canvasParent.offsetWidth, canvasParent.offsetHeight).parent(canvasParent);
buttonY = height - buttonHeight - 10;
}
//Draw Images (Video Frames)
function draw() {
clear();
background(0);
frameRate(FPS);
if (isImage) {
image(uploadedItemSrc, 0, 0, width, height);
}
if (isVideo) {
//Add your logic here before video frame is loaded on canvas
image(uploadedItemSrc, 0, 0, width, height);
if(isPlaying){
//Add your logic here after video frame is loaded on canvas
tint(random(0,255),random(0,255),random(0,255));
}
//Draw Play Button
fill(200);
rect(buttonX, buttonY, buttonWidth, buttonHeight);
fill(0);
textAlign(CENTER, CENTER);
textSize(16);
text(isPlaying ? "Pause" : "Play", buttonX + buttonWidth / 2, buttonY + buttonHeight / 2);
}
}
function readFile(file) {
var fileExtension = file.subtype.toLowerCase();
var uploadedItems = select('#uploadedItems');
// If Image
if (fileExtension.startsWith("png")) {
let img = createImg(file.data);
img.parent(uploadedItems);
img.mouseClicked(function() {
console.log("Getting Image Information!");
loadImage(img.elt.src, function(loadedImage) {
uploadedItemSrc = loadedImage;
isImage = true;
isVideo = false;
});
});
}
// If Video
else if (fileExtension.startsWith("mp4")) {
let video = createVideo(file.data);
video.parent(uploadedItems);
video.mouseClicked(function() {
console.log("Getting Video Information!");
uploadedItemSrc = video;
isImage = false;
isVideo = true;
});
}
}
function mouseClicked() {
if (mouseX > buttonX && mouseX < buttonX + buttonWidth && mouseY > buttonY && mouseY < buttonY + buttonHeight) {
if (uploadedItemSrc && uploadedItemSrc instanceof p5.Element) {
if (uploadedItemSrc.elt.paused) {
uploadedItemSrc.elt.play();
isPlaying = true;
} else {
uploadedItemSrc.elt.pause();
isPlaying = false;
}
}
}
}
#uploadedItems img,
#uploadedItems video {
width: 100px;
height: 70px;
margin: 10px;
border: 1px solid black;
}
<!DOCTYPE html>
<html>
<head>
<link href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/css/bootstrap.min.css" rel="stylesheet">
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="61115421504f554f50">[email protected]</a>/lib/p5.min.js"></script>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-12 p-0">
<div class="text-center dropzone w-100 fs-3 border rounded-3 p-4" id="dropzone">
Drag your file here <br>
<small class="fs-6">.mp4 or .png</small>
</div>
</div>
</div>
<div class="row mt-3">
<div class="col-md-6 pl-1 pr-1">
<div id="uploadedItems" class="d-flex flex-wrap border rounded-3 border-1 p-2" style="height: 200px; overflow-y: auto;">
<!-- Uploaded items will be displayed here -->
</div>
</div>
<div class="col-md-6 p-0 border rounded-3" id="canvasParent">
</div>
</div>
</div>
</body>
</html>