Skip to content

Commit

Permalink
Merge pull request #35 from Octoframes/dynamic_images
Browse files Browse the repository at this point in the history
Implemented Dynamic Image Updates
  • Loading branch information
christopher-besch authored Aug 1, 2022
2 parents 7156c42 + 8bdd6b3 commit 17ac4ec
Show file tree
Hide file tree
Showing 10 changed files with 253 additions and 13 deletions.
26 changes: 26 additions & 0 deletions public/dynamic_imgs/drag_drop.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.imgs_wrapper {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 10px;
}

.img {
width: 200px;
height: 200px;
display: flex;
justify-content: center;
align-items: center;

border-color: black;
border-style: solid;
}

.dragover {
border-style: dashed;
border-color: red;
}

.image_preview {
width: 100%;
}
97 changes: 97 additions & 0 deletions public/dynamic_imgs/drag_drop.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// based on https://soshace.com/the-ultimate-guide-to-drag-and-drop-image-uploading-with-pure-javascript

function load_from_disk(files, callback) {
if (files.length != 1)
console.log("Warning: only using first image");

let reader = new FileReader();
reader.onload = (e) => {
let url = e.target.result;
callback(url);
}
reader.readAsDataURL(files[0]);
}

function load_from_img_element(data) {
let html = data.getData("text/html");
let match = html && /\bsrc="?([^"\s]+)"?\s*/.exec(html);
// TODO: use quiet fail
if (!match)
throw "Failed to load url from dragged object.";
let url = match[1];
return url;
}

function get_valid_urls(image_urls) {
return image_urls.filter(url => url);
}

function kill_all_children(parent) {
while (parent.lastChild) {
parent.removeChild(parent.lastChild);
}
}

function load_new_url(target, url, image_urls, on_new_imgs, image_id) {
image_urls[image_id] = url;
console.log(`new image at index ${image_id}: ${url}`);
on_new_imgs(get_valid_urls(image_urls));

// update image
kill_all_children(target);
let img = document.createElement("img");
img.src = url;
img.classList.add("image_preview")
target.appendChild(img);
}

function handle_drop(image_urls, e, on_new_imgs, image_id) {
let data = e.dataTransfer;
let files = data.files;
// drag+drop from user file system
if (files.length)
load_from_disk(files, (url) => {
load_new_url(e.target, url, image_urls, on_new_imgs, image_id);
});
// drag+drop from HTMLImageElement (or invalid drop (e.g. text))
else {
let url = load_from_img_element(data);
load_new_url(e.target, url, image_urls, on_new_imgs, image_id);
}
}

function prevent_default(e) {
e.preventDefault();
e.stopPropagation();
}

function enable_style_change(image) {
// don't you fucking dare touch this
// this garbage took me an hour to not glitch around like a berserk all the time
image.addEventListener("dragover", () => {
image.classList.add("dragover");
});
image.addEventListener("dragleave", () => {
image.classList.remove("dragover");
});
image.addEventListener("drop", () => {
image.classList.remove("dragover");
});
}

function load_img_drop(image_urls, on_new_imgs) {
let images = document.getElementsByClassName("img");
for (let i = 0; i < images.length; ++i) {
images[i].addEventListener("dragenter", prevent_default, false);
images[i].addEventListener("dragleave", prevent_default, false);
images[i].addEventListener("dragover", prevent_default, false);
images[i].addEventListener("drop", prevent_default, false);

images[i].addEventListener("drop", (e) => {
handle_drop(image_urls, e, on_new_imgs, i);
});

enable_style_change(images[i]);
}
}

68 changes: 68 additions & 0 deletions public/dynamic_imgs/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<!DOCTYPE html>
<html lang="en">

<head>
<title>compare_view Dynamic Images</title>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<meta name="description" content="" />
<link rel="stylesheet" href="./drag_drop.css">
</head>

<body>
<script src="../dist/browser_compare_view.js"></script>
<script src="./drag_drop.js"></script>

<img src="../images/banner_colour.png" width=500 />
<img src="../images/banner_grey.png" width=500 />

<div class="imgs_wrapper">
<div class="img">
<div>Drag&amp;Drop Image Here</div>
</div>
<div class="img">
<div>Drag&amp;Drop Image Here</div>
</div>
<div class="img">
<div>Drag&amp;Drop Image Here</div>
</div>
<div class="img">
<div>Drag&amp;Drop Image Here</div>
</div>
</div>

<div style="display: flex; flex-direction: row; width: 100%;">
<div>
<canvas id="canvas" style="width: 100%;"></canvas>
</div>
<div id="controls" style="width: 120px;">
</div>
</div>
<script>
compare_view.load(
[
"../images/cat.png",
"../images/cat_grey.png",
],
"canvas",
{
controls_id: "controls",
},
(cvd) => {
load_img_drop([
"../images/cat.png",
"../images/cat_grey.png",
undefined,
undefined,
undefined,
], (image_urls) => {
// TODO: remove debug
console.log(image_urls);
compare_view.load_new_imgs(cvd, image_urls);
});
}
);
</script>
</body>

</html>
12 changes: 11 additions & 1 deletion src/browser/compare_view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { load_cvd } from "../load_cvd";
import { attach_control_data } from "../controls";
import { load_ctx } from "./canvas";
import { create_controls } from "./create_controls";
import { CompareViewData } from "../compare_view_data";
import { load_new_imgs } from "../images";

export interface BrowserConfig extends Config {
// leave undefined to not create controls
Expand All @@ -12,7 +14,12 @@ export interface BrowserConfig extends Config {
key?: string;
}

export function load(image_urls: string[], canvas_id: string, config: BrowserConfig = {}) {
export function load(
image_urls: string[],
canvas_id: string,
config: BrowserConfig = {},
callback: (cvd: CompareViewData) => void = () => { }
): void {
// create controls before image loading
let ctrl_data = config.controls_id != undefined ? create_controls(config.controls_id, config.key) : undefined;
load_cvd(
Expand All @@ -22,7 +29,10 @@ export function load(image_urls: string[], canvas_id: string, config: BrowserCon
(cvd) => {
if (ctrl_data != undefined)
attach_control_data(cvd, ctrl_data);
callback(cvd);
},
)
}

export { load_new_imgs };

9 changes: 9 additions & 0 deletions src/compare_view_data.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Config } from "./cfg";

export enum Mode {
undefined = "undefined",
horizontal = "horizontal",
Expand All @@ -9,6 +11,7 @@ export enum Task {
// don't do anything, just render once
none,
revolve_imgs,
update_imgs,
// switch current_mode to next_mode
change_mode,
// update circle position and start rendering
Expand Down Expand Up @@ -38,10 +41,16 @@ export interface ControlData {

export interface CompareViewData {
// general //
cfg: Config;

images: Image[];
// number of images doesn't change
images_len: number;

// when updating images
new_images: Image[];
new_image_resolution: [number, number];

canvas: HTMLCanvasElement;
ctx: CanvasRenderingContext2D;
width: number;
Expand Down
8 changes: 6 additions & 2 deletions src/component/compare_view.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// export { default as ReactFromModule } from "react"
import { Config } from "../cfg";
import React, { useEffect } from "react";
import { load_cvd } from "../load_cvd";
import { attach_control_data } from "../controls";
import { ControlData } from "../compare_view_data";
import { CompareViewData, ControlData } from "../compare_view_data";
import { load_new_imgs } from "../images";

interface ComponentConfig extends Config {
create_controls?: boolean;
Expand All @@ -12,6 +12,7 @@ interface ComponentConfig extends Config {
interface CompareViewProps {
image_urls: string[];
config?: ComponentConfig;
callback?: (cvd: CompareViewData) => void;
}

const CompareView: React.FC<CompareViewProps> = (props) => {
Expand All @@ -36,6 +37,8 @@ const CompareView: React.FC<CompareViewProps> = (props) => {
};
attach_control_data(cvd, ctrl_data);
}
if (props.callback != undefined)
props.callback(cvd);
});
}, []);

Expand Down Expand Up @@ -70,3 +73,4 @@ const CompareView: React.FC<CompareViewProps> = (props) => {

export default CompareView;

export { load_new_imgs };
20 changes: 19 additions & 1 deletion src/images.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { CompareViewData, Image } from "./compare_view_data";
import { CompareViewData, Image, Task } from "./compare_view_data";
import { load_canvas_scaling } from "./scaling";
import { push_task } from "./task_solver";

function get_img_resolution(images: Image[]): [number, number] {
let width = images[0]?.element.width as number;
Expand Down Expand Up @@ -44,3 +46,19 @@ export function revolve_imgs(cvd: CompareViewData): boolean {
return true;
}

export function update_images(cvd: CompareViewData): boolean {
cvd.images = cvd.new_images;
load_canvas_scaling(cvd, cvd.new_image_resolution);
return true;
}

// TODO: accept HTMLImageElement
export function load_new_imgs(cvd: CompareViewData, image_urls: string[]): void {
load_images(image_urls, (images, img_resolution) => {
cvd.new_images = images;
cvd.images_len = images.length;
cvd.new_image_resolution = img_resolution;
push_task(cvd, Task.update_imgs);
});
}

7 changes: 6 additions & 1 deletion src/load_cvd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,14 @@ export function load_cvd(image_urls: string[], ctx: CanvasRenderingContext2D, cf
load_images(image_urls, (images, img_resolution) => {
// used to e.g. scale circle correctly
let cvd: CompareViewData = {
cfg: cfg,

images: images,
images_len: images.length,

new_images: [],
new_image_resolution: [0, 0],

canvas: ctx.canvas,
ctx: ctx,
// to be changed later
Expand Down Expand Up @@ -58,7 +63,7 @@ export function load_cvd(image_urls: string[], ctx: CanvasRenderingContext2D, cf
start_pos: 0,
target_pos: 0,
};
load_canvas_scaling(cvd, img_resolution, cfg);
load_canvas_scaling(cvd, img_resolution);

// e.g. let caller attach controls
if (prerun_callback != undefined)
Expand Down
14 changes: 7 additions & 7 deletions src/scaling.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { choose_cfg, Config } from "./cfg";
import { CompareViewData, Task } from "./compare_view_data";
import { CompareViewData } from "./compare_view_data";

// needs to get called every time canvas size changes
function load_fractions(cvd: CompareViewData, cfg: Config): void {
function load_fractions(cvd: CompareViewData): void {
let max_size = Math.max(cvd.canvas.width, cvd.canvas.height);
cvd.circumference_thickness = max_size * choose_cfg(cfg, "circumference_fraction");
cvd.circumference_thickness = max_size * choose_cfg(cvd.cfg, "circumference_fraction");
// use circle_size when provided, otherwise use fraction
cvd.circle_size = cfg.circle_size != undefined ? cfg.circle_size : max_size * choose_cfg(cfg, "circle_fraction");
cvd.slider_thickness = max_size * choose_cfg(cfg, "slider_fraction");
cvd.circle_size = cvd.cfg.circle_size != undefined ? cvd.cfg.circle_size : max_size * choose_cfg(cvd.cfg, "circle_fraction");
cvd.slider_thickness = max_size * choose_cfg(cvd.cfg, "slider_fraction");
}

export function load_canvas_scaling(cvd: CompareViewData, img_resolution: [number, number], cfg: Config): void {
export function load_canvas_scaling(cvd: CompareViewData, img_resolution: [number, number]): void {
cvd.canvas.width = img_resolution[0];
cvd.canvas.height = img_resolution[1];
cvd.width = cvd.canvas.width;
cvd.height = cvd.canvas.height;

load_fractions(cvd, cfg);
load_fractions(cvd);
}
5 changes: 4 additions & 1 deletion src/task_solver.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Mode, CompareViewData, Task } from "./compare_view_data";
import { revolve_imgs } from "./images";
import { revolve_imgs, update_images } from "./images";
import { change_mode } from "./modes/change_mode";
import { remove_circle, update_circle, render_circle } from "./modes/circle_mode";
import { instant_slide, render_slider, start_slider_move, update_slider } from "./modes/slider_mode";
Expand All @@ -22,6 +22,9 @@ function solve_tasks(cvd: CompareViewData, timestamp: number): void {
case Task.revolve_imgs:
handled = revolve_imgs(cvd);
break;
case Task.update_imgs:
handled = update_images(cvd);
break;
case Task.update_circle:
handled = update_circle(cvd);
break;
Expand Down

0 comments on commit 17ac4ec

Please sign in to comment.