330 lines
8.8 KiB
JavaScript
330 lines
8.8 KiB
JavaScript
const ShelfPack = require('@mapbox/shelf-pack');
|
|
const jQuery = require('jquery');
|
|
const fabric = require('fabric').fabric;
|
|
require('fabric-customise-controls');
|
|
const margin = 10;
|
|
let canvas = null;
|
|
let iscircle = false; // circle canvas shape
|
|
let isgrid = false; // grid layout
|
|
let globalbin = [];
|
|
|
|
function onimgload(event) {
|
|
let img = new Image();
|
|
img.src = event.target.result;
|
|
jQuery("#masonrydraglibrary")[0].appendChild(img);
|
|
jQuery("body").removeClass("drag");
|
|
jQuery("#pack").click();
|
|
}
|
|
/**
|
|
* Drag and drop. Dropping an image adds it to the #masonrydraglibrary.
|
|
*/
|
|
jQuery(document).on("drop", function(ev) {
|
|
ev.stopPropagation();
|
|
ev.preventDefault();
|
|
let dt = ev.originalEvent.dataTransfer;
|
|
if (dt.items) {
|
|
// Use DataTransferItemList interface to access the file(s)
|
|
for (let i=0; i < dt.items.length; i++) {
|
|
if (dt.items[i].kind == "file") {
|
|
let f = dt.items[i].getAsFile();
|
|
let fr = new FileReader();
|
|
fr.onload = onimgload;
|
|
fr.readAsDataURL(f);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
jQuery(document).on("dragend", function(ev) {
|
|
ev.stopPropagation();
|
|
ev.preventDefault();
|
|
});
|
|
jQuery(document).on("dragenter", function(ev) {
|
|
ev.stopPropagation();
|
|
ev.preventDefault();
|
|
jQuery("body").addClass("drag");
|
|
});
|
|
jQuery(document).on("dragover", function(ev) {
|
|
ev.stopPropagation();
|
|
ev.preventDefault();
|
|
});
|
|
|
|
jQuery(document).ready(function() {
|
|
jQuery("#masonryslide-container").hide();
|
|
jQuery("#masonrylibrary").hide();
|
|
jQuery("#masonrydraglibrary").hide();
|
|
/*
|
|
fabric.Canvas.prototype.customiseControls({
|
|
mb: { // middle bottom
|
|
action: function (e, target) {
|
|
console.log(target);
|
|
target._element.cropX(50);
|
|
canvas.renderAll();
|
|
}
|
|
},
|
|
}, function() {
|
|
canvas.renderAll();
|
|
} );
|
|
*/
|
|
});
|
|
jQuery(document).on('click', '#square', function() {
|
|
jQuery("button.btn-secondary").removeClass("active");
|
|
jQuery(this).addClass("active");
|
|
window.cwidth = jQuery(".container").width();
|
|
window.cheight = window.cwidth;
|
|
jQuery("#masonryslide").html("<canvas id='canvase'></canvas>");
|
|
});
|
|
jQuery(document).on('click', '#rectangle', function() {
|
|
jQuery("button.btn-secondary").removeClass("active");
|
|
jQuery(this).addClass("active");
|
|
window.cwidth = jQuery(".container").width();
|
|
window.cheight = window.cwidth * 1.5;
|
|
jQuery("#masonryslide").html("<canvas id='canvase'></canvas>");
|
|
});
|
|
jQuery(document).on('click', '#circle', function() {
|
|
jQuery("button.btn-secondary").removeClass("active");
|
|
jQuery(this).addClass("active");
|
|
window.cwidth = jQuery(".container").width();
|
|
window.cheight = window.cwidth;
|
|
iscircle = true;
|
|
jQuery("#masonryslide").html("<canvas id='canvase'></canvas>");
|
|
});
|
|
|
|
// Read an image from file input
|
|
async function readImage(file) {
|
|
return new Promise((resolve, reject) => {
|
|
let fr = new FileReader();
|
|
fr.onload = (event) => {
|
|
let img = document.createElement('img');
|
|
img.src = event.target.result;
|
|
img.onload = function() {
|
|
globalbin.push({
|
|
data: this,
|
|
width: this.width,
|
|
height: this.height,
|
|
size: this.width * this.height
|
|
});
|
|
resolve();
|
|
};
|
|
};
|
|
fr.readAsDataURL(file);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Drag & drop is over, now with regular packing
|
|
*/
|
|
// Aspect ratio
|
|
jQuery(document).on('click', '#pack', async function() {
|
|
let cheight, cwidth, i, len, ref;
|
|
jQuery("#masonryslide-container").show();
|
|
let library = jQuery("#masonrylibrary");
|
|
library.empty();
|
|
library.css({
|
|
position: 'absolute',
|
|
left: '9999px'
|
|
}).show();
|
|
globalbin = [];
|
|
if (window.cwidth === undefined) {
|
|
window.cwidth = 660;
|
|
}
|
|
if (window.cwidth > jQuery("#masonryslide-container").width()){
|
|
window.cwidth = jQuery("#masonryslide-container").width();
|
|
}
|
|
if (window.cheight === undefined) {
|
|
window.cheight = 1000;
|
|
}
|
|
cwidth = window.cwidth;
|
|
cheight = window.cheight;
|
|
jQuery("#canvase").width(cwidth).height(cheight);
|
|
canvas = new fabric.Canvas("canvase");
|
|
canvas.setDimensions({
|
|
width: cwidth,
|
|
height: cheight
|
|
});
|
|
|
|
ref = document.getElementById('masonryslide-files').files;
|
|
if (ref.length > 0) {
|
|
for (i = 0, len = ref.length; i < len; i++) {
|
|
await readImage(ref[i]);
|
|
}
|
|
}
|
|
|
|
let images = document.querySelectorAll("#masonrydraglibrary img");
|
|
if (images.length > 0) {
|
|
for (i = 0, len = images.length; i < len; i++) {
|
|
let image = images[i];
|
|
let width = jQuery(image).width();
|
|
let height = jQuery(image).height();
|
|
globalbin.push({
|
|
data: image,
|
|
width: width,
|
|
height: height,
|
|
size: width * height
|
|
});
|
|
}
|
|
}
|
|
library.hide();
|
|
|
|
if (!isgrid) {
|
|
globalbin.sort(function(a,b){
|
|
return b.size - a.size;
|
|
});
|
|
}
|
|
|
|
if (iscircle) {
|
|
circlepack (globalbin, canvas);
|
|
} else {
|
|
pack (globalbin, canvas);
|
|
}
|
|
});
|
|
|
|
/**
|
|
* The actual packing.
|
|
*/
|
|
function pack (bin, canvas) {
|
|
const cwidth = canvas.width;
|
|
const cheight = canvas.height;
|
|
let i, len, j, len2;
|
|
let sprite = new ShelfPack(cwidth - margin, cheight - margin);
|
|
|
|
if (isgrid || bin.length <= 4) {
|
|
// desired width and height for every image
|
|
const dwidth = Math.floor((cwidth - margin) / Math.sqrt(bin.length));
|
|
const dheight = Math.floor((cheight - margin) / Math.sqrt(bin.length));
|
|
for (i = 0, len = bin.length; i < len; i++) {
|
|
if (bin[i].width > bin[i].height) {
|
|
bin[i].w = dwidth + margin;
|
|
bin[i].h = bin[i].height * (dwidth / bin[i].width) + margin;
|
|
} else {
|
|
bin[i].w = bin[i].width * (dheight / bin[i].height) + margin;
|
|
bin[i].h = dheight + margin;
|
|
}
|
|
bin[i].id = i;
|
|
bin[i] = packone(sprite, cwidth, cheight, bin[i]);
|
|
}
|
|
} else {
|
|
let size = 0;
|
|
for (i = 0; i < 4; i++) {
|
|
if (bin[i] === undefined) {
|
|
break;
|
|
}
|
|
size += bin[i].size;
|
|
}
|
|
const ratio = (cwidth * cheight) / size;
|
|
for (i = 0, len = bin.length; i < len; i++) {
|
|
bin[i].w = bin[i].width * ratio + margin;
|
|
bin[i].h = bin[i].height * ratio + margin;
|
|
bin[i].id = i;
|
|
bin[i] = packone(sprite, cwidth, cheight, bin[i]);
|
|
}
|
|
}
|
|
|
|
for (i = 0, len = bin.length; i < len; i++) {
|
|
let x = bin[i].x + margin;
|
|
let y = bin[i].y + margin;
|
|
let imgInstance = new fabric.Image(bin[i].data, {
|
|
left: x,
|
|
top: y,
|
|
angle: 0,
|
|
});
|
|
if (bin[i].width > bin[i].height) {
|
|
imgInstance.scaleToWidth(bin[i].w - margin);
|
|
} else {
|
|
imgInstance.scaleToHeight(bin[i].h - margin);
|
|
}
|
|
canvas.add(imgInstance);
|
|
}
|
|
}
|
|
|
|
function packone(sprite, cwidth, cheight, bin) {
|
|
let pbin = null;
|
|
let tsprite = sprite;
|
|
while (pbin === null) {
|
|
pbin = tsprite.packOne(bin.w, bin.h, bin.id);
|
|
if (pbin === null) {
|
|
bin.w = (bin.w - margin) / 2 + margin;
|
|
bin.h = (bin.h - margin) / 2 + margin;
|
|
tsprite = sprite;
|
|
}
|
|
}
|
|
bin.x = pbin.x;
|
|
bin.y = pbin.y;
|
|
bin.w = pbin.w;
|
|
bin.h = pbin.h;
|
|
return bin;
|
|
}
|
|
|
|
function circlecoords (width, height, t) {
|
|
return {
|
|
x: width * Math.cos(t),
|
|
y: height * Math.sin(t)
|
|
};
|
|
}
|
|
function circlepack(bin, canvas) {
|
|
const cwidth = canvas.width;
|
|
const cheight = canvas.height;
|
|
let i, len, t;
|
|
const area = (cwidth * cheight / 4) * Math.PI;
|
|
const ratio = area / bin[0].size * bin.length;
|
|
const linestep = Math.PI / 2 / Math.floor(ratio);
|
|
|
|
i = 0;
|
|
// A rectangular line in the circle
|
|
for (t = Math.PI * 0.75 ; t > Math.PI / 2; t = t - linestep) {
|
|
if (bin[i] === undefined) {
|
|
break;
|
|
}
|
|
let opposite = 0;
|
|
if ( t < Math.PI ) {
|
|
opposite = t - Math.PI / 2;
|
|
} else if (t > Math.PI) {
|
|
opposite = t + Math.PI / 2;
|
|
}
|
|
let endcoords = circlecoords(cwidth, cheight, opposite);
|
|
let coords = {};
|
|
// A series of images inside this line, limited by opposite end of the circle
|
|
while (coords.x < endcoords.x && coords.y < endcoords.y) {
|
|
let imgInstance = new fabric.Image(bin[i].data, coords);
|
|
imgInstance.scaleToWidth(bin[i].width * ratio);
|
|
imgInstance.scaleToHeight(bin[i].height * ratio);
|
|
canvas.add(imgInstance);
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
jQuery(document).on('click', '#repack', function() {
|
|
if (iscircle) {
|
|
circlerepack(canvas);
|
|
} else {
|
|
repack(canvas);
|
|
}
|
|
});
|
|
|
|
function repack(canvas) {
|
|
let bin = canvas.getObjects();
|
|
const cwidth = canvas.width;
|
|
const cheight = canvas.height;
|
|
for (i = 0, len = bin.length; i < len; i++) {
|
|
bin[i].w = bin[i].width * bin[i].scaleX + margin;
|
|
bin[i].h = bin[i].height * bin[i].scaleY + margin;
|
|
}
|
|
bin.sort(function(a,b){
|
|
return b.width * b.height - a.width * a.height;
|
|
});
|
|
let sprite = new ShelfPack(cwidth, cheight);
|
|
sprite.pack(bin, {
|
|
inPlace: true
|
|
});
|
|
for (i = 0, len = bin.length; i < len; i++) {
|
|
bin[i].scaleToWidth(bin[i].w - margin);
|
|
bin[i].scaleToHeight(bin[i].h - margin);
|
|
bin[i].setPositionByOrigin(
|
|
new fabric.Point(bin[i].x, bin[i].y),
|
|
'left',
|
|
'top'
|
|
);
|
|
}
|
|
canvas.renderAll();
|
|
}
|