canvas setDimension() breaks drag functionality of figure - javascript

I am using draw2dtouch 6.1.66 version (latest version). Initially width and height of canvas was 1000px and 800px respectively. On button click, I am changing the canvas width and height to 2170px and 902px respectively.
Now I cannot drag rectangle figure beyond 1000px width-wise. It gets stuck to 1000px which was initial width.
Note: I check my html, both canvas div and svg shows width:2170px and height:902px.
<div _ngcontent-awi-17="" draw2d-canvas="" id="canvas" style="height: 902px; cursor: default; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); width: 2170px;">
<svg height="902" version="1.1" width="2170" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="overflow: hidden; position: absolute; left: -0.828125px;">
Here is my code:
$("#canvas").css("height",902);
$("#canvas").css("width", 2170);
canvas.setDimension(new draw2d.geo.Rectangle(0, 0, 2170, 902));

It may be and old post, but I had the same issue using same version, and my solution might be useful for some other people coming back.
It seems when the canvas is created Canvas.regionDragDropConstraint is initialized with the original size of the canvas, restrincting the area in which objects can be dragged inside. I had to modify the method Canvas.setDimension, and added the update of the attribute, so when resized, it's updated in all the figures referencing the same object.
/**
* #method
* Tells the canvas to resize. If you do not specific any parameters
* the canvas will attempt to determine the height and width by the enclosing bounding box
* of all elements and set the dimension accordingly. If you would like to set the dimension
* explicitly pass in an draw2d.geo.Rectangle or an object with <b>height</b> and <b>width</b> properties.
*
* #since 4.4.0
* #param {draw2d.geo.Rectangle} [dim] the dimension to set or null for autodetect
*/
setDimension: function(dim, height)
{
if (typeof dim === "undefined"){
var widths = this.getFigures().clone().map(function(f){ return f.getAbsoluteX()+f.getWidth();});
var heights = this.getFigures().clone().map(function(f){ return f.getAbsoluteY()+f.getHeight();});
this.initialHeight = Math.max.apply(Math,heights.asArray());
this.initialWidth = Math.max.apply(Math,widths.asArray());
}
else if(dim instanceof draw2d.geo.Rectangle){
this.initialWidth = dim.w;
this.initialHeight = dim.h;
}
else if(typeof dim.width ==="number" && typeof dim.height ==="number"){
this.initialWidth = dim.width;
this.initialHeight = dim.height;
}
else if(typeof dim ==="number" && typeof height ==="number"){
this.initialWidth = dim;
this.initialHeight = height;
}
this.html.css({"width":this.initialWidth+"px", "height":this.initialHeight+"px"});
this.paper.setSize(this.initialWidth, this.initialHeight);
this.setZoom(this.zoomFactor, false);
// MODIFICATION START TO CORRECT DRAG AND DROP PROBLEM
// Add modification to the Region Drap and Drop Policy, so it does not restricts objects movements on resize.
this.regionDragDropConstraint.constRect.w = this.getWidth();
this.regionDragDropConstraint.constRect.h = this.getHeight();
// MODIFICATION END
return this;
}

Setting the width and height of a canvas using css, will result in scale like effect, because the underlying imageData array is not updated to the new dimensions.
So if you need to scale your canvas, use the attributes of the canvas, like so:
$('#canvas').attr({width: <width>, height: <height>});
Keep in mind that changing those values will clear the canvas and you need to redraw everything.

Related

How to align an element to the bottom of the window using JS

I have created a html-5 banner using Adobe Animate and have an element, which should be aligned to the bottom of the window depending on the height of this window.
My banner should be 100% height and on the first picture you can see the right position of the bottom elements. And on the second picture there is a space between the element and the background.
And my element should remain aligned to the bottom when changing the height of the window. How can I do it using js?
Here is my code which helps me move the element but it doesn't align it to the bottom:
this.frame_0 = function() {
var _second = this.second;
this.addEventListener("tick", res.bind(this));
function res() {
_second.y = window.innerHeight/2 + 120;
}
}
This is my file
use CSS to set the positions of element.
position:fixed;
left:ValueFromLeft;
bottom:0;
It's easy
If use CreateJS then you are using a canvas.
CSS solutions don't work, only JS.
Use the Window's resize event.
this.frame_0 = function() {
var _second = this.second;
function setSecondToTheBottom() {
_second.y = window.innerHeight - (_second.nominalBounds.height / 2);
}
setSecondToTheBottom();
window.addEventListener("resize", setSecondToTheBottom);
};

How to determine the height and width of HTML user input and tell gridster to fit accordingly?

I'm currently using Gridster.js (http://gridster.net/) in combination with CKEditor.
Once the user saves their content with CKEditor, this content is put into the widget. However the widgets do not automatically resize themselves to fit the content, and while the user is able to resize it themselves, it would be more convienient for the userbase to have it be done for them the moment they press save.
I have tried a few things, but none to any avail. I'm having trouble getting the size of the current content, and then resizing the gridster respectively.
In my code, I have two values to work with. the gridster element (widget), and the value that will be put into it (contents). I have to determine the height of the contents. Once this is done successfully, I will be able to determine if my code for getting the x and y values work.
My current code looks like this:
// Initialization of the gridster element.
// The base dimensions are relevant to understand how we
// calculate the multipliers, later on.
gridster = $(".gridster > ul").gridster({
widget_margins: [10, 10],
widget_base_dimensions: [100, 50],
draggable: {
handle: 'header'
},
resize: {
enabled: true,
max_size: [20, 10],
min_size: [2, 1]
}
}).data('gridster');
And (the relevant bits of) my JavaScript class that handles saving and resizing:
// Saves the content from CKEditor to the gridster widget
this.save = function (data) {
var lastContents = this.default_html + data + '</div>';
$(this.editor).removeClass('gs-w-new');
this.resize_widget(this.editor, lastContents);
$(this.editor).html(lastContents);
this.modal.modal('hide');
};
/* #TODO: resize_widget function */
// if the new content from ckeditor is larger than the
// original size of the widget, we need to make it larger.
this.resize_widget = function(widgetId, contents) {
var element = $('<div>')
.addClass('fake-div-gs-w-resize')
/*
.fake-div-gs-w-resize {
position: absolute;
display: none;
height: auto;
width: auto;
white-space: nowrap;
}
*/
.css('display', 'block')
.html(contents);
var widget = $(widgetId);
var elementWidth = $(element).width(), // I am expecting this to return the width of the content, but it returns 0.
elementHeight = $(element).height(), // As you might imagine, this also returns 0.
width = widget.width(),
height = widget.height();
$(element).css('display', 'none');
console.log(widgetId, widget, width, height, elementWidth, elementHeight);
// this code never gets past here, because element{Height,Width} returns 0.
if (elementHeight > height || elementWidth > width) {
var width_multiplier = 100, // data-x = 1 === width_multiplier px
height_multiplier = 50; // from "widget_base_dimensions: [100, 50],"
var x = Math.round(width / width_multiplier),
y = Math.round(height / height_multiplier),
eX = Math.ceil(elementWidth / width_multiplier),
eY = Math.ceil(elementHeight / height_multiplier);
console.log("setting to x:" + eX + ", y:" + eY + " with width:" + width + ", height:" + height);
if (eX >= x && eY >= y)
gridster.resize_widget(widget, eX, eY);
}
};
Whilst I am not completely confident in my logic for determining the sizes; the main focus of this question is with determining the size of the HTML contents, as what I gathered from other SO posts did not seem to help in my case.
You need to actually add the element to the DOM for the width() and height() functions to work. In your example, the element is not added to the document.
See this JS Fiddle as an example https://jsfiddle.net/y1yf1zzp/10/
I had the same challenge, i.e. dynamic content appearing inside the new tile caused an overflow and appeared outside the tile boundaries. We used the '.scrollHeight' of the tile contents in combination with Zartus' code:
var contentHeight = $widgit.firstChild.scrollHeight;
var tileHeight = $widgit.firstChild.clientHeight;

Position and Drag An Element With A Transform Applied

I have a javascript code that works perfectly for dragging object...but when I scaled the body down to 0.5...
transform:scale(0.5);
the position of mouse and the object dragged is not the same. How can I fix this? or is this possible?... thank you.
Heres a fiddle : http://jsfiddle.net/Xcb8d/65/
This was pretty interesting and makes me rethink all the locations I simply used offsetHeight and offsetWidth without knowing that if a transformation was applied to the element, these readonly properties in JavaScript would return incorrect.
The "trick" to this is the clientHeight/offsetHeight will not report their transformed properties correctly. In order to get the correct sizing information from the element you need to call getBoundingClientRect(). You then can calculate the correct pixel information of the scaled element, allowing you then to perform the correct positioning.
Retrieving the bounding rectangle allows you to get the pixel information off the viewport, you then can compare this information to the clientHeight within the browser to determine the scaled offset height, and position.
I modified some of the event wire ups just for testing. Also I added another class to produce a quarter sized object just to prove it works regardless of scale.
CSS:
html,
body {
width:100%;
height:100%;
}
.half-size
{
transform:scale(0.5);
-moz-transform:scale(0.5);
-webkit-transform:scale(0.5);
}
.quarter-size
{
transform:scale(0.25);
-moz-transform:scale(0.25);
-webkit-transform:scale(0.25);
}
#draggable-element {
width:100px;
height:100px;
background-color:#666;
color:white;
padding:10px 12px;
cursor:move;
position:relative; /* important (all position that's not `static`) */
display:block;
}
JavaScript:
var selected = null, // Object of the element to be moved
x_pos = 0, y_pos = 0, // Stores x & y coordinates of the mouse pointer
x_elem = 0, y_elem = 0; // Stores top, left values (edge) of the element
var elem_height = 0;
var elem_width = 0;
// Will be called when user starts dragging an element
function _drag_init(elem) {
// Store the object of the element which needs to be moved
selected = elem;
var boundingRectangle = selected.getBoundingClientRect();
y_elem = (selected.offsetHeight - (boundingRectangle.bottom - boundingRectangle.top)) / 2;
x_elem = (selected.offsetWidth - (boundingRectangle.right - boundingRectangle.left)) / 2
half_elem_height = (boundingRectangle.bottom - boundingRectangle.top) / 2;
half_elem_width = (boundingRectangle.right - boundingRectangle.left) /2;
document.addEventListener('mousemove', _move_elem, false);
document.addEventListener('mouseup', _destroy, false);
};
// Will be called when user dragging an element
function _move_elem(e)
{
x_pos = e.clientX;
y_pos = e.clientY;
selected.style.left = ((x_pos - x_elem) - half_elem_width) + 'px';
selected.style.top = ((y_pos - y_elem) - half_elem_height) + 'px';
}
// Destroy the object when we are done and remove event binds
function _destroy() {
selected = null;
document.removeEventListener('mousemove', _move_elem);
document.removeEventListener('mouseup', _destroy);
}
// Bind the functions...
document.getElementById('draggable-element').onmousedown = function () {
_drag_init(this);
};
HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
</head>
<body>
<div id="draggable-element" class='half-size'>Drag me!</div>
</body>
</html>
Click below for a live preview
http://jsbin.com/moyadici/1/edit
(I prefer jsBin over jsFiddle for its live updates)
I did modify some of the event wire ups just for my initial testing. Also I broke the transform into a style so I could try other transforms. Tests look correct when rendering the 'quarter-size'. This just proves out it works regardless of scale and you don't need a magic number for your position calculations.
Not a real answer , but too long for a comment and because it is still jumpy, maybe not on first try ...
In Firefox, using pointer-events, you could try to restrain the area that will catch the mouse.
create an inside element right in center wich has the size of the element itself once scaled.
Your fiddle says scale(0.5) a square of 100px, so lets draw a square of 50px right in the middle with a pseudo element.
Set pointer-events to none to the element and reset it back to normal for the pseudo element. Now we have a square of 50px that will accept the mouse. once the element scaled down to 50px , we still have this area reacting. (the square only looks smaller, but keeping using same space.
To finalize your fiddle test, let's add to body : transform-origin:top left;, now we should be abble to drag this square.
Firefox test :http://jsfiddle.net/Xcb8d/78/
Chrome test : http://jsfiddle.net/Xcb8d/79/ (negative margins added )
after a few clicks, it really gets jumpy of limits :)
hope it gives some hints.
reading this : http://css-tricks.com/get-value-of-css-rotation-through-javascript/
I thought , we could get the transform value from body and update calculation within the function , so we can modify scale value without touching script
. rookie test : http://jsfiddle.net/Xcb8d/82/
ex: function updated from original fiddle
// Will be called when user dragging an element
function _move_elem(e) {
var el = window.document.body;
var st = window.getComputedStyle(el, null);
var tr = st.getPropertyValue("transform") ;
var values = tr.split('(')[1];
values = values.split(')')[0];
values = values.split(',');
var a = values[0];
var b = values[1];
var c = values[2];
var d = values[3];
x_pos = document.all ? window.event.clientX : e.pageX;
y_pos = document.all ? window.event.clientY : e.pageY;
if (selected !== null) {
selected.style.left = ((x_pos / a) - x_elem) + 'px';
selected.style.top = ((y_pos / d) - y_elem) + 'px';
}
}

Calculating offset for HTML5 video

I am tracing a point in an HTML5 video using canvas overlay. The canvas is on top of the video tag with the following style:
#my-canvas { width: 100%; height: 100%; position:absolute !important; z-index:100000; margin: 0 auto; background-color:rgba(68, 170, 213, 0.0); }
The points were captured via separate (Android) app. Here's how I plot the points on the canvas:
MyClass.prototype.drawLine = function(_context, _ctr, _color) {
_context.lineWidth = 2;
_context.lineJoin = 'round';
_context.strokeStyle = _color;
_context.moveTo(this.data["xs"][_ctr]*this.xOffset, this.data["ys"][_ctr]*this.yOffset);
_context.lineTo(this.data["xs"][_ctr+1]*this.xOffset, this.data["ys"][_ctr+1]*this.yOffset);
_context.stroke();
_context.closePath();
}
I multiply the data points with an offset value to compensate for the screen size. This is how I calculate the offset:
MyClass.prototype.calculateOffsets = function() {
this.yOffset = this.video.offsetHeight/parseFloat(this.data["height"]);
this.xOffset = this.video.offsetWidth/parseFloat(this.data["width"]);
}
This code works fine in a landscape video but not in portrait. I might be missing something with the offset calculation. I would appreciate if you can point me to the proper direction.
Thanks in advance.
When you set canvas size using CSS your canvas will actually always be 300x150 pixels. What you draw to it will be relative to that size.
Try instead to remove the 100% width and height from the CSS rule and set the canvas size for its bitmap every time you get an onresize event etc. CSS sets the element size; this will set the bitmap size (element will follow if no CSS is overriding):
_context.canvas.width = window.innerWidth;
_context.canvas.height = window.innerHeight;
and
#my-canvas {width: 100%; height: 100%;position:absolute...
Notice though that changing the canvas size will also clear it so you will have to redraw everything up to that point if that is needed.

Creating masks across browsers

I see that there are ways to do this in webkit browsers, but I don't see ways to do it in others. Is this simply a feature that hasn't been implemented in all the browsers?
I don't have standard images, so clip won't work. I may have to render everything ahead of time, which will make my work exponential, but you deal with what you have, right?
I'd also want to be able to activate this stuff from javascript. :/
Thanks if you can provide support.
Just off the top of my head - and without an actual problem from you for us to solve - here's a possible way to accomplish what you want...
HTML
<div class="myImage">
<img src="path_to_image" title="Lorem ipsum" alt="Dolar sit amet" />
<div class="myMask">
</div><!-- /myMask -->
</div><!-- /myImage -->
CSS
.myImage {
position: relative;
}
.myMask {
position:absolute;
top: 0;
left: 0;
background-color: transparent;
background-image: url('path_to_masking_image');
}
Alternatively, use an <img /> inside the myMask div, and remove the background-image property from the CSS.
The way it's currently laid out, you would need two images: the image itself in all its glory, and the mask.
The way you would accomplish the 'masking effect' is to have the mask image be a static solid color that matches background of the container its in - ie white, black, whatever.
Kapeesh? This would work in all browsers, which is what you asked for. The background-clip property has -webkit and -moz selectors, but is not supported in browsers like IE or (to my knowledge) Opera.
Here are my 2 cents, if it is indeed CSS Sprites you are after.
<head>
<style type="text/css"><!--
#imagemask {
background-image: url(image.png);
background-repeat: no-repeat;
background-color: transparent;
height: 40px;
width: 40px;
}
.mask1 { background-position: top left; }
.mask2 { background-position: 0 40px; }
.mask3 { background-position: 0 80px; }/* And so on, however your image file is 'layed out' */
--></style>
<script type="text/javascript">
function mask1(){ document.getElementById("imagemask").setAttribute("class", "mask1"); }
function mask2(){ document.getElementById("imagemask").setAttribute("class", "mask1"); }
function mask3(){ document.getElementById("imagemask").setAttribute("class", "mask1"); }
</script>
</head>
<body>
mask 1
mask 2
mask 3
<div id="imagemask" class="mask1"></div>
</body>
We define the div#imagemask to contain 1 image file as a background and set it to not repeat around, as that would sort of defy the point.
We define how to "move around" the image inside the "mask" (div) with fixed width and height.
As a reference, I've then added the javascript you need to switch between the masks on the fly. I wrote that in about 10 seconds, you could probably write something a little more elegant if you want.
Add the links with onclick= events
Finally, add the div#imagemask to the body.
Given that I don't know the width or height of your image file or it's target masking, you'll have to do some substantial edits to this code. But you get the idea :)
I'm just going to skip the CSS variant in favor of this:
Example of a working mask: http://gumonshoe.net/NewCard/MaskTest.html
I acquired a javascript class from another website tutorial:
http://gumonshoe.net/js/canvasMask.js
It reads the image data and applies the alpha pixels from the mask to the target image:
function applyCanvasMask(image, mask, width, height, asBase64) {
// check we have Canvas, and return the unmasked image if not
if (!document.createElement('canvas').getContext) return image;
var bufferCanvas = document.createElement('canvas'),
buffer = bufferCanvas.getContext('2d'),
outputCanvas = document.createElement('canvas'),
output = outputCanvas.getContext('2d'),
contents = null,
imageData = null,
alphaData = null;
// set sizes to ensure all pixels are drawn to Canvas
bufferCanvas.width = width;
bufferCanvas.height = height * 2;
outputCanvas.width = width;
outputCanvas.height = height;
// draw the base image
buffer.drawImage(image, 0, 0);
// draw the mask directly below
buffer.drawImage(mask, 0, height);
// grab the pixel data for base image
contents = buffer.getImageData(0, 0, width, height);
// store pixel data array seperately so we can manipulate
imageData = contents.data;
// store mask data
alphaData = buffer.getImageData(0, height, width, height).data;
// loop through alpha mask and apply alpha values to base image
for (var i = 3, len = imageData.length; i < len; i = i + 4) {
imageData[i] = alphaData[i];
}
// return the pixel data with alpha values applied
if (asBase64) {
output.clearRect(0, 0, width, height);
output.putImageData(contents, 0, 0);
return outputCanvas.toDataURL();
}
else {
return contents;
}
}

Categories