P5 Drag and Drop not loading - drag-and-drop

I have been trying to implement a load canvas function using the drag and drop method explained in the P5 reference below. However on the first instance of dragging an image to the canvas, the image will not load. The second attempt will load the image fine. This happens with every 'new' image that I try to load to the canvas.
https://p5js.org/examples/dom-drop.html
When commenting out the '.hide' you can see that the image data is successfully loading on the first attempt. I am a little confused as to how I correct this issue.
Thank you to anyone that can point me in the right direction.
index.html
<!DOCTYPE html>
<html>
<head>
<script src="p5.js"></script>
<script src="p5.dom.js"></script>
<script src="sketch.js"></script>
</head>
<body>
</body>
</html>
sketch.js
function setup() {
// create canvas
var c = createCanvas(710, 400);
background(100);
// Add an event for when a file is dropped onto the canvas
c.drop(gotFile);
}
function draw() {
fill(255);
noStroke();
textSize(24);
textAlign(CENTER);
text('Drag an image file onto the canvas.', width/2, height/2);
noLoop();
}
function gotFile(file) {
// If it's an image file
if (file.type === 'image') {
// Create an image DOM element but don't show it
var img = createImg(file.data).hide();
// Draw the image onto the canvas
image(img, 0, 0, width, height);
} else {
println('Not an image file!');
}
}

My guess is that this line takes some time to process the image in the background:
var img = createImg(file.data).hide();
So when you immediately try to use img, it's not always done processing.
image(img, 0, 0, width, height);
One workaround for this is to not use the img right away. Here's a simple example:
var img;
function setup() {
var c = createCanvas(710, 400);
c.drop(gotFile);
}
function draw() {
if(img){
image(img, 0, 0, width, height);
}
}
function gotFile(file) {
if (file.type === 'image') {
img = createImg(file.data).hide();
}
}

Related

How can I pass a dynamic height to Leaflet CRS map in my Ionic 3 App?

I am using Leaflet to render a floor plan map on my page. I am able to render my image, but because a user can upload any size of map, I need to be able to account for that.
I am capturing the height/width when the image is uploaded, but not sure how to pass that to my view.
I am trying this (without much luck):
<div #map id="map" ng-style="{'height' : '{{ myObject.mapHeight }}px' }"></div>
mapHeight is available in my .ts file. and I can output it to the view to check that a value is actually coming through. What is the best way to pass the image height to the view?
Here is my .ts file:
map: L.Map = null;
#ViewChild('map') mapContainer: ElementRef;
...
this.map = L.map(this.mapContainer.nativeElement, {
crs: L.CRS.Simple,
});
let geoJson = L.geoJSON(this.geoData, {
style: function (feature) {
return {
weight: 0.8
}
},
onEachFeature: function (feature, layer) {
//
}
}).addTo(this.map);
let bounds: any = [[0, 0], [this.myObject.mapHeight, this.myObject.mapWidth]];
let image: any = L.imageOverlay(this.myObject.mapUrl, bounds).addTo(this.map);
this.map.fitBounds(bounds);
EDIT
If I do this, I can get the height, but watching the logs, it seems this method just runs and runs and runs. I can immagine at some point I'll consume all memory and that's no good.
<div #map id="map" [ngStyle]="getMapStyle()"></div>
.ts
...
getMapStyle() {
console.log('--> getMapStyle called');
console.log('map height: ', this.mapHeight + 'px');
return {
'height': this.myObject.mapHeight + 'px'
};
}
For anyone else trying to do the same, here is what ended up working for me.
.html
<div #map id="map" [style.height.px]="myObject?.mapHeight"></div>

Drag and Drop into Fabric.js canvas

How can I drop items (like image, or other object from other canvas) into canvas which is managed by fabricjs? I have found many examples how to move items inside canvas but I would like to drag and drop item from outer element into canvas.
Since you asked for an example and I haven't tried it out myself yet, here goes:
Example Fiddle
Markup
<div id="images">
<img draggable="true" src="http://i.imgur.com/8rmMZI3.jpg" width="250" height="250"></img>
<img draggable="true" src="http://i.imgur.com/q9aLMza.png" width="252" height="295"></img>
<img draggable="true" src="http://i.imgur.com/wMU4SFn.jpg" width="238" height="319"></img>
</div>
<div id="canvas-container">
<canvas id="canvas" width="800" height="600"></canvas>
</div>
JS Breakdown
1. Fabric.canvas instance
First we want our canvas, of course:
var canvas = new fabric.Canvas('c');
2. Feature Detection (optional)
Not sure this is necessary, since the fact that you have a canvas makes it very likely that the browser has Drag and Drop as well. Were you to use it, you can do so like this, using Modernizr:
if (Modernizr.draganddrop) {
// Browser supports HTML5 DnD.
// Bind the event listeners for the image elements
// Bind the event listeners for the canvas
} else {
// Replace with a fallback to a library solution.
alert("This browser doesn't support the HTML5 Drag and Drop API.");
}
3. Events
Again, unlike the source article I below, the source and target elements are different (in that articles's example, you just move divs around within the same parent container), so I failed to notice that some of the events are meant for the element being dragged, but most are bound to the element into which you are dropping.
NOTE: I know this is technically a question about Fabric.js, but it's really kind of a question about Drag and Drop in the context of adding objects to a <canvas> with Fabric.js, which is why I'm going a bit more in depth about the DnD stuff now.
For the <img>
dragstart (I added a class here to lower the opacity)
dragend (and removed that class here)
For #canvas-container:
dragenter (added a class to give the canvas container that nifty dotted line)
dragover: Here you can set the event.dataTransfer.dropEffect property to show one of the native cursor types. The default would be 'move' here, but I set it to 'copy' since I don't actually remove the <img> element (in fact in the fiddle you can, for example create several McClures).
dragleave (removed the dotted line here)
drop: The handler for this event creates and adds the fabric.Image object (see the fiddle).
if (Modernizr.draganddrop) {
// Browser supports HTML5 DnD.
// Bind the event listeners for the image elements
var images = document.querySelectorAll('#images img');
[].forEach.call(images, function (img) {
img.addEventListener('dragstart', handleDragStart, false);
img.addEventListener('dragend', handleDragEnd, false);
});
// Bind the event listeners for the canvas
var canvasContainer = document.getElementById('canvas-container');
canvasContainer.addEventListener('dragenter', handleDragEnter, false);
canvasContainer.addEventListener('dragover', handleDragOver, false);
canvasContainer.addEventListener('dragleave', handleDragLeave, false);
canvasContainer.addEventListener('drop', handleDrop, false);
} else {
// Replace with a fallback to a library solution.
alert("This browser doesn't support the HTML5 Drag and Drop API.");
}
Sources:
HTML5 Rocks - Native HTML5 Drag and Drop
Modernizr
Web Platform Docs > DOM > Properties - dropEffect
Web Platform Docs > DOM > Events
dragstart
dragend
dragenter
dragover
dragleave
drop
I had gone through fiddle of #natchiketa, And fixed the problem , just check this fiddle..
http://jsfiddle.net/Ahammadalipk/w8kkc/185/
window.onload = function () {
var canvas = new fabric.Canvas('canvas');
/*
NOTE: the start and end handlers are events for the <img> elements; the rest are bound to
the canvas container.
*/
function handleDragStart(e) {
[].forEach.call(images, function (img) {
img.classList.remove('img_dragging');
});
this.classList.add('img_dragging');
}
function handleDragOver(e) {
if (e.preventDefault) {
e.preventDefault();
}
e.dataTransfer.dropEffect = 'copy';
return false;
}
function handleDragEnter(e) {
this.classList.add('over');
}
function handleDragLeave(e) {
this.classList.remove('over');
}
function handleDrop(e) {
if (e.stopPropagation) {
e.stopPropagation(); // stops the browser from redirecting.
}
var img = document.querySelector('#images img.img_dragging');
var newImage = new fabric.Image(img, {
width: img.width,
height: img.height,
// Set the center of the new object based on the event coordinates relative
// to the canvas container.
left: e.layerX,
top: e.layerY
});
newImage.hasControls = newImage.hasBorders = false;
canvas.add(newImage);
return false;
}
function handleDragEnd(e) {
// this/e.target is the source node.
[].forEach.call(images, function (img) {
img.classList.remove('img_dragging');
});
}
if (Modernizr.draganddrop) {
var images = document.querySelectorAll('#images img');
[].forEach.call(images, function (img) {
img.addEventListener('dragstart', handleDragStart, false);
img.addEventListener('dragend', handleDragEnd, false);
});
var canvasContainer = document.getElementById("canvas-container");
canvasContainer.addEventListener('dragenter', handleDragEnter, false);
canvasContainer.addEventListener('dragover', handleDragOver, false);
canvasContainer.addEventListener('dragleave', handleDragLeave, false);
canvasContainer.addEventListener('drop', handleDrop, false);
} else {
alert("This browser doesn't support the HTML5 Drag and Drop API.");
}
}
Thanks
Well, the question is quite old^^
I have updateted the fiddle, no it will work in Firefox as well.
Fiddle
function handleDrop(e) {
// this / e.target is current target element.
e.preventDefault(); //I've altert this line for FireFox
As far as I understand, drag and drop is not already provided by fabricjs,
fiddling it will be quite interesting.
Well I m new to javascript and fabricJS but I think this fiddle might help
fiddle
html code
<div class="img_cont">
<img class="img" id="ele1" draggable="true" ondragstart="dragElement(event)" src="https://webkit.org/wp-content/uploads/STP-300x300.png">
<img class="img" id="ele2" draggable="true" ondragstart="dragElement(event)" src="https://webkit.org/wp-content/uploads/ephy-webkit-graphic.png">
<img class="img" id="ele3" draggable="true" ondragstart="dragElement(event)" src="https://res.cloudinary.com/css-tricks/image/upload/w_600,q_auto,f_auto/buysellads/uu/7/112766/1646327381-MC_CSSTricks_Logo_600x600-_1_.png">
<img class="img" id="ele4" draggable="true" ondragstart="dragElement(event)" src="https://miro.medium.com/max/1400/1*9hd_8qR0CMZ8L0pVbFLjDw.png">
</div>
<br>
<div id="canvas_cont" ondragover="allowDrop(event)" ondrop="dropElement(event)">
<canvas id="canvas" width="650" height="350" ></canvas>
</div>
javascript code
// allowDrop function called on ondragover event.
function allowDrop(e) {
e.preventDefault();
}
//dragElement function called on ondrag event.
function dragElement(e) {
e.dataTransfer.setData("id", e.target.id); //transfer the "data" i.e. id of the target dragged.
}
//Initializing fabric canvas on window load event.
var canvas;
window.onload = function(){
canvas = new fabric.Canvas(document.getElementById("canvas"));
}
//dropElement function called on ondrop event.
function dropElement(e) {
e.preventDefault();
var data = e.dataTransfer.getData("id"); //receiving the "data" i.e. id of the target dropped.
var imag = document.getElementById(data); //getting the target image info through its id.
var img = new fabric.Image(imag, { //initializing the fabric image.
left: e.layerX - 80, //positioning the target on exact position of mouse event drop through event.layerX,Y.
top: e.layerY - 40,
});
img.scaleToWidth(imag.width); //scaling the image height and width with target height and width, scaleToWidth, scaleToHeight fabric inbuilt function.
img.scaleToHeight(imag.height);
canvas.add(img);
}
The accepted answer no longer works.
This is for drag and drop from desktop using the dataTransfer interface.
canvas.on('drop', function(event) {
// prevent the file to open in new tab
event.e.stopPropagation();
event.e.stopImmediatePropagation();
event.e.preventDefault();
// Use DataTransfer interface to access the file(s)
if(event.e.dataTransfer.files.length > 0){
var files = event.e.dataTransfer.files;
for (var i = 0, f; f = files[i]; i++) {
// Only process image files.
if (f.type.match('image.*')) {
// Read the File objects in this FileList.
var reader = new FileReader();
// listener for the onload event
reader.onload = function(evt) {
// put image on canvas
fabric.Image.fromURL(evt.target.result, function(obj) {
obj.scaleToHeight(canvas.height);
obj.set('strokeWidth',0);
canvas.add(obj);
});
};
// Read in the image file as a data URL.
reader.readAsDataURL(f);
}
}
}
});
Resources
https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/File_drag_and_drop

D3 filtering data points

I'm implementing the classic mercator example (https://github.com/mbostock/d3/blob/master/examples/mercator/mercator.html), which I've changed to zoom into Afghanistan and to use only one custom slider. I'm reading in GeoJSON data of places where explosions have happened and the graph maps them all at load. I want to use the slider to view only a month of explosion points at a time but am having trouble filtering the results. I've tried several things based on posts in the Google group but fail to understand how to filter the data read in previously from 'explosions.json'. Thanks for the help!
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<title>IED Attacks in Afghanistan (2004-2009)</title>
<script type="text/javascript" src="../d3.v2.js"></script>
<script type="text/javascript" src="../lib/jquery/jquery.min.js"></script>
<script type="text/javascript" src="../lib/jquery-ui/jquery-ui.min.js"></script>
<style type="text/css">
#import url("../lib/jquery-ui/jquery-ui.css");
body, .ui-widget {
font: 14px Helvetica Neue;
}
svg {
width: 960px;
height: 600px;
border: solid 1px #ccc;
background: #eee;
}
line {
stroke: brown;
stroke-dasharray: 4,2;
}
path {
fill: #ccc;
stroke: #fff;
}
div {
width: 960px;
}
</style>
</head>
<body>
<h3>IED Attacks in Afghanistan (2004-2009)</h3>
<script type="text/javascript">
// Create the Mercator Projection (Map)
var xy = d3.geo.mercator(),
path = d3.geo.path().projection(xy);
// Create the states variable
var states = d3.select("body")
.append("svg")
.append("g")
.attr("id", "states");
// Create the equator variable
var equator = d3.select("svg")
.append("line")
.attr("x1", "0%")
.attr("x2", "100%");
// Create the explosions variable
var explosions = d3.select("svg")
.append("g")
.attr("id","explosions");
// Load in the states & equator data from the file 'world-countries.json'
d3.json("world-countries.json", function(collection) {
states
.selectAll("path")
.data(collection.features)
.enter().append("path")
.attr("d", path)
.append("title")
.text(function(d) { return d.properties.name; });
equator
.attr("y1", xy([0, 0])[1])
.attr("y2", xy([0, 0])[1]);
});
// the variable that holds our translate, center on Afghanistan
var translate = xy.translate(); //create translation to center gride in different area
translate[0] = -1741;
translate[1] = 1487;
xy.translate(translate); // center
xy.scale(12000); //zoom in
// Load in the explosions data from the file 'explosions.json'
d3.json("explosions.json", function(collection) {
explosions
.selectAll("path") //make a path and attach data
.data(collection.features)
.enter().append("path")
.attr("d", path)
.style("stroke","red") //color the path points
.style("stroke-width",2) //size of point stroke
.attr("class","explosionpoint")
.append("title") //title is the 'name' field in the json file
.text(function(d) { return d.properties.name; });
});
</script>
<p></p>
<!-- Slider -->
<div id="scale"></div><p></p>
<script type="text/javascript">
$("#scale").slider({
min: 20040101, //min : 1/1/04
max: 20100101, //max: 1/1/10
value: 20060601, //default slider value
step: 100, // step is the allow increments the slider can move. 100 = one month
slide: function(event, ui) {
/* REMOVE ALL EXPLOSION PATHS EXCEPT FOR A PARTICULAR MONTH OR RELOAD WITH FILTERED RESULTS */
}
});
</script>
You'll need to post part or all of your explosions.json object for a concrete answer. However, something like this will filter a JSON if it's structured like {explosion1:{data1:true, data2:true}, explosion2:{data1:true, data2:false}}:
function filterJSON(json, key, value) {
var result = {};
for (var explosionIndex in json) {
if (json[explosionIndex][key] === value) {
result[explosionIndex] = json[explosionIndex];
}
}
return result;
}
(e.g. filterJSON(myjson, "data1", true) will give all explosions with data1:true)
This is not specific to d3.
Then you could use something like this for the d3-side of things:
explosions.data(myFilteredData).exit().remove(); // remove ones you don't want
explosions.enter().append("path")... // add back ones you do want
If I understand your application, it would actually be better to just toggle the visiblity attribute of the SVG elements.
var sliderrange = [20040101, 20040201]; //replace with code based on your slider
explosions.selectAll(".explosionpoint").attr("visibility", function(d) {
//Replace with the correct date comparison logic
return d.date < sliderrange[1] && d.date > sliderrange[0] ? "visible" : "hidden";
});
D3 does have a very natural way of doing this. I'll assume your data looks something like this:
[{name: explosion1name, day: 20040110,...}, {name: explosion2name, day: 20040111,...}]
...and that you've got some variable, we'll call it explosionsData, to reference the data.
You can then draw your explosions with a function that takes the values from your slider. See the .filter I've added below.
function drawExplosions(startDay, endDay) {
explosions.selectAll("path") //make a path and attach data
.data(collection.features)
.enter().append("path")
.filter( function (d) { return ( (d.day > startDay) && (d.day < endDay) )})
.attr("d", path)
.style("stroke","red") //color the path points
.style("stroke-width",2) //size of point stroke
.attr("class","explosionpoint")
.append("title") //title is the 'name' field in the json file
.text(function(d) { return d.properties.name; });
Just call this function whenever your slider values changes.

Change default photoset size on tumblr

I know tumblr has the defualt photoset sizes {photoset-500},{photoset-400},{photoset-250}, but I want to change the width, so that the width could be either 420, or 350. i have looked everywhere and cannot find a code to help me do this.
You can use {Photoset} which just resizes the photoset to fit the container (max size 700px).
https://www.tumblr.com/docs/en/custom_themes
You cannot change default sizes of images with Tumblr markup, but you can visually change size with CSS.
Tumblr markup:
{block:Photoset}
{block:Photos}
<img src="{PhotoURL-500}" class="photoset-img" />
{/block:Photos}
{/block:Photoset}
CSS:
.photoset-img { width: 420px; /* can be any value you want */ }
Add this script before the ending body tag. This will increase or decrease the size of the photoset
<script type="text/javascript">
//This will change the source address and display the correct size.
$(".photoset").each(function() {
var newSrc = $(this).attr("src").replace('700','860');
$(this).attr("src", newSrc);
});
//This will get the new size of the iframe and resize the iframe holder accordingly.
$(function(){
var iFrames = $('.photoset');
function iResize() {
for (var i = 0, j = iFrames.length; i < j; i++) {
iFrames[i].style.height = iFrames[i].contentWindow.document.body.offsetHeight + 'px';}
}
if ($.browser.safari || $.browser.opera) {
iFrames.load(function(){
setTimeout(iResize, 0);
});
for (var i = 0, j = iFrames.length; i < j; i++) {
var iSource = iFrames[i].src;
iFrames[i].src = '';
iFrames[i].src = iSource;
}
} else {
iFrames.load(function() {
this.style.height = this.contentWindow.document.body.offsetHeight + 'px';
});
}
});
</script>

Fancybox Positioning Inside Facebook Canvas iFrame

OK so I have a iframe canvas app with its height set to "Settable" with the facebook javascrip sdk calls to FB.Canvas.setSize(); and FB.Canvas.setAutoGrow();. These are working perfectly, as the iframe gets set to a certain pixel height based on its content.
The problem is that when I make a call to Fancybox, it positions itself based on this height. I know that's exactly what its supposed to do as the fancybox jQuery returns the viewport by:
(line 673 of latest version of jquery.fancybox-1.3.4.js):
_get_viewport = function() {
return [
$(window).width() - (currentOpts.margin * 2),
$(window).height() - (currentOpts.margin * 2),
$(document).scrollTop() + currentOpts.margin
];
},
But the problem is the iframe will, for a lot of viewers, be longer than their browser window. So the Fancybox centers itself in the iframe and ends up only partly visible to the majority of viewers. (i.e. iframe height is 1058px and users browser is say only 650px).
Is there a way to have fancybox just calculate the physical browser height? Or do I need to change some settings in my Facebook canvas app to make it work?
I like how the only scrollbar is the one on Facebook (the parent, if you will).
All suggestions GREATLY appreciated!
For fancybox 2 try:
find:
_start: function(index) {
and replace with:
_start: function(index) {
if ((window.parent != window) && FB && FB.Canvas) {
FB.Canvas.getPageInfo(
function(info) {
window.canvasInfo = info;
F._start_orig(index);
}
);
} else {
F._start_orig(index);
}
},
_start_orig: function (index) {
Then in function getViewport replace return rez; with:
if (window.canvasInfo) {
rez.h = window.canvasInfo.clientHeight;
rez.x = window.canvasInfo.scrollLeft;
rez.y = window.canvasInfo.scrollTop - window.canvasInfo.offsetTop;
}
return rez;
and finally in _getPosition function replace line:
} else if (!current.locked) {
with:
} else if (!current.locked || window.canvasInfo) {
As facebook js api provides page info, then we could use it, so
find
_start = function() {
replace with
_start = function() {
if ((window.parent != window) && FB && FB.Canvas) {
FB.Canvas.getPageInfo(
function(info) {
window.canvasInfo = info;
_start_orig();
}
);
} else {
_start_orig();
}
},
_start_orig = function() {
and also modify _get_viewport function
_get_viewport = function() {
if (window.canvasInfo) {
console.log(window.canvasInfo);
return [
$(window).width() - (currentOpts.margin * 2),
window.canvasInfo.clientHeight - (currentOpts.margin * 2),
$(document).scrollLeft() + currentOpts.margin,
window.canvasInfo.scrollTop - window.canvasInfo.offsetTop + currentOpts.margin
];
} else {
return [
$(window).width() - (currentOpts.margin * 2),
$(window).height() - (currentOpts.margin * 2),
$(document).scrollLeft() + currentOpts.margin,
$(document).scrollTop() + currentOpts.margin
];
}
},
I had the same problem, i used 'centerOnScroll' :true, and now it works fine...
Had the same problem. Thankfully fancybox is accessable through CSS. My solution was to overwrite fancybox's positioning in my CSS file:
#fancybox-wrap {
top: 20px !important;
}
This code places the fancybox always 20px from top of the iframe. Use a different size if you like. The !important sets this positioning even though fancybox sets the position dynamically at runtime.
Here's one way to do it by positioning the Fancybox relative to the position of another element, in my case an Uploadify queue complete div that displays a view link after the user uploads an image.
Have a style block with a set ID like so:
<style id="style-block">
body { background-color: #e7ebf2; overflow: hidden; }
</style>
Then the link to open the Fancybox calls a function with the image name, width, and height to set the content and sizes. The important part is the positioning. By getting the position of the queue complete div, generating a new class declaration (fancy-position), appending it to the style block BEFORE the fancybox loads (!important in class will override positioning from fancybox), then adding the new class using the wrapCSS parameter in the fancybox options, it positions the fancybox exactly where I want it.
function viewImage(image, width, height) {
var complete_pos = $('#image_queue_complete').position();
var css_code = '.fancy-position { top: ' + complete_pos.top.toString() + 'px !important; }';
$('#style-block').append(css_code);
var img_src = '<img src="images/' + image + '" width="' + width.toString() + '" height="' + height.toString() + '" />';
$.fancybox({
content: img_src,
type: 'inline',
autoCenter: false,
wrapCSS: 'fancy-position'
});
}