I can use my raphael test page (just playing with it at the moment) in Firefox and IE 11 (eurgh). It doesn't, however, work in Chrome. I've checked the console and no errors have been reported, just the Raphael features won't work (adding a rect, image).
So, firstly:
1. Is chromes support for Raphael different to others?
Secondly, I'll post my code in case I've made a huge error somewhere (left out HTML as it's a basic DIV).
//Get the background image
var myImg = new Image();
myImg.src = 'comp1.bmp';
var width = myImg.width;
var height = myImg.height;
var allShapes = [];
var paper = new Raphael("paper", width, height);
//Add a background image
var img = paper.image("comp1.bmp", 0, 0, width, height);
var cw = width / 2;
var ch = height / 2;
var allShapes = [];
var activeRect; //This will store the rect that was used last.
//drag and drop code
function addNewShape(){
var rectDetails = {};
rectDetails.rect = paper.rect(cw - 50, ch - 50, 50, 50);
rectDetails.rect.attr({
'stroke': 'orange',
'fill': 'white',
'fill-opacity': 0,
'stroke-width': 2
});
//Set the resizer for this rectangle
var resize = paper.rect(cw - 20, ch - 20, 20, 20);
resize.attr({
fill: "orange",
stroke: "none",
opacity: .5
});
//Assign to rect
resize.box = rectDetails.rect;
resize.drag(move_resizer, start_resizer);
rectDetails.rect.sizer = resize;
rectDetails.rect.drag(drag_move, drag_start, drag_up);
//Add co-ordinate text box
var div = document.getElementById('coordContainer');
div.innerHTML = div.innerHTML + "Shape " + rectDetails.rect.id + ": <input type='text' id='pos" + rectDetails.rect.id + "'>";
//Add the new shape to the shape coords array
allShapes[rectDetails.rect.id] = rectDetails.rect;
}
function getCoords(){
var coordDiv = document.getElementById('resultsContainer');
for(var i = 1; i <= allShapes.length; i++) {
var bbox = allShapes[i].getBBox();
var temp = "Shape " + allShapes[i].id + ": X(" + bbox.x + "), Y(" + bbox.y + ")";
coordDiv.innerHTML = coordDiv.innerHTML + temp;
}
}
var ox = null;
var oy = null;
function drag_start(e) {
//Set the active rect
activeRect = this;
//Assign attrs
this.ow = this.attr('width');
this.oh = this.attr('height');
this.ox = this.attr('x');
this.oy = this.attr('y');
this.sizer.ox = this.sizer.attr("x");
this.sizer.oy = this.sizer.attr("y");
this.sizer.attr({opacity: 1});
};
//Moving the shape
function drag_move(dx, dy, posx, posy) {
document.getElementById("pos" + this.id).value = "X: " + dx + " | Y: " + dy; //co-ordinates of the shape!
this.attr({
transform: "...T" + (dx - ox) + "," + (dy - oy)
});
this.sizer.attr({x: this.sizer.ox + dx, y: this.sizer.oy + dy});
// store the previous versions of dx,dy for use in the next move call
ox = dx;
oy = dy;
}
function drag_up(e) {
//reset the original positions
ox = 0;
oy = 0;
}
function start_resizer(){
// storing original coordinates
this.ox = this.attr("x");
this.oy = this.attr("y");
this.box.ow = this.box.attr("width");
this.box.oh = this.box.attr("height");
}
function move_resizer(dx, dy){
// move will be called with dx and dy
this.attr({x: this.ox + dx, y: this.oy + dy});
this.box.attr({width: this.box.ow + dx, height: this.box.oh + dy});
}
NB:
The addShape() function is called from a button.
Chrome shows the div, just it's content is empty (white).
I have no idea what the issue is with this code. Any help greatly received.
Related
I'm having trouble to get the correct position to items added to the graph after moving items to a new area on the left side of the paper.
Function called when the item is drop on the canvas.
function drop(ev) {
ev.preventDefault();
// CANVAS ELEMENT DROP
var data = ev.dataTransfer.getData("text");
console.log('ev.pageX: ' + ev.pageX);
console.log('$(paper-container).offset().left: ' + $('#paper-container').offset().left);
console.log('$(.joint-paper).position().left: ' + $('.joint-paper').position().left);
console.log('ev.pageY: ' + ev.pageY);
console.log('$(paper-container).offset().top: ' + $('#paper-container').offset().top);
console.log('$(.joint-paper).position().top: ' + $('.joint-paper').position().top);
var x = ev.pageX - $('#paper-container').offset().left - $('.joint-paper').position().left;
var y = ev.pageY - $('#paper-container').offset().top - $('.joint-paper').position().top;
addCanvasItem(x, y);
}
Then adding item to the canvas:
function addCanvasItem(x, y) {
//x and y are in the screen referential
var zoomRatio = vm.paperScroller.zoom();
var scrollX = vm.paperScroller.el.scrollLeft;
var scrollY = vm.paperScroller.el.scrollTop;
var posX = x / zoomRatio + scrollX / zoomRatio - 50;
var posY = y / zoomRatio + scrollY / zoomRatio - 30;
item.attributes.position = { x: posX, y: posY };
item.addTo(vm.graph);
var cellView = item.findView(vm.paper);
rappidJsConfig.resetSelection([cellView.model]);
vm.selectedView = cellView;
vm.initEditor();
if (vm.isVisibleType('qad.Question')) {
var selectedQuestion = document.getElementById("selected-question");
if (selectedQuestion) {
selectedQuestion.focus();
}
}
};
Any ideas ?
Let's say I have a canvas that is split into a 15x10 32-pixel checkboard. Thus, I have this:
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
var tileSize = 32;
var xCoord
var yCoord
var tilesX = 15; // tiles across
var tilesY = 10; // tiles up and down
var counted = 1; // for drawing purpose for checkerboard for visual guidance
var mouseSel = new Image()
mouseSel.src = 'http://i.imgur.com/vAA03NB.png' // mouse selection
mouseSel.width = 32
mouseSel.height = 32
function isOdd(num) {
return num % 2;
}
function getMousePos(canvas, evt) {
// super simple stuff here
var rect = canvas.getBoundingClientRect();
return {
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
};
}
drawCanvas(); // upon intilization... draw
function drawCanvas() {
for (var y = 0; y <= 10; y++) {
for (var x = 0; x <= 15; x++) {
if (isOdd(counted)) {
context.fillStyle = '#dedede'
context.fillRect(x * 32, y * 32, 32, 32);
// checkboard drawn complete.
}
counted++;
} // end first foor loop
counted++;
} // end last for loop
if (counted >= 176) counted = 1 // once all tiles (16x11) are drawn... reset counter for next instance
}
canvas.addEventListener('mousemove', function (evt) {
context.clearRect(0, 0, canvas.width, canvas.height); // clear canvas so mouse isn't stuck
drawCanvas(); // draw checkboard
// get the actual x,y position of 15x10 32-pixel checkboard
var mousePos = getMousePos(canvas, evt);
mousePos.xCoord = Math.floor(mousePos.x / tileSize)
mousePos.yCoord = Math.floor(mousePos.y / tileSize)
// draw the mouse selection
context.drawImage(mouseSel, (mousePos.xCoord * 32), (mousePos.yCoord * 32), 32, 32) // draw mouse selection
// debug
var message = ' (' + mousePos.xCoord + ',' + mousePos.yCoord + ') | (' + mousePos.x + ',' + mousePos.y + ')';
var textarea = document.getElementById('debug');
textarea.scrollTop = textarea.scrollHeight;
$('#debug').append(message + '\n');
}, false);
canvas#canvas {
background: #ABABAB;
position: relative;
z-index: 1;
float: left;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="canvas" height="352" width="512" tabindex="0"></canvas>
<textarea name="" id="debug" cols="30" rows="35"></textarea>
**NOTE: ** Make sure to scroll down in that preview pane so you can see the debug textarea.
As you can see, the event of "drawing" fires EVERY single time it moves. That means every pixel.
I am trying to figure out how to make the drawing fire ONLY when a new x,y coord has changed. Because it'd be useless to redraw the mouse selection when it's only moved 5 pixels across and it's still going to be drawn at the same place.
My suggestion
Upon entering, have a temporary value and when that is passed, to redraw again?
Make a temporary value and update that if it was different from before. Then put the code in an if statement where either have changed.
var tempX, tempY;
var newX = 100;
var newY = 100;
tempX = mousePos.xCoord;
tempY = mousePos.yCoord;
if (newX !== tempX || newY !== tempY) {
// code here
}
if (tempX !== newX) newX = mousePos.xCoord;
if (tempY !== newY) newY = mousePos.yCoord;
JSFiddle: http://jsfiddle.net/weka/bvnma354/8/
I'm trying to rotate an element around another element. I've put this code together, and even though the code works fine, I have a little problem with the element's rotation. It's not very accurate. The blue dot leaves the orbit when at the bottom.
I've played with the code but couldn't fix it. You can check the fiddle example.
rotateObj function taken from Felix Eve's answer.
function markCircleCenter(circ, orig) {
if (typeof circ == "string") {
var elm = document.getElementById(circ);
} else { var circle = circ; }
if (typeof orig == "string") {
var mark = document.getElementById(orig);
} else { var elm = orig; }
var width = parseInt(window.getComputedStyle(elm).width, 10);
var height = parseInt(window.getComputedStyle(elm).height, 10);
var top = parseInt(window.getComputedStyle(elm).top, 10);
var left = parseInt(window.getComputedStyle(elm).left, 10);
var markWidth = parseInt(window.getComputedStyle(mark).width, 10);
var markHeight = parseInt(window.getComputedStyle(mark).height, 10);
var circle = {
circle: elm,
x: width/2 + left,
y: height/2 + top,
};
mark.style.position = "absolute";
mark.style.top = circle.y - markWidth/2 + "px";
mark.style.left = circle.x - markHeight/2 + "px";
document.body.insertBefore(mark, elm.nextSibling);
return circle;
}
function placeObjIntoOrbit(obj) {
if (typeof obj == "string") {
var obj = document.getElementById(obj);
} else { var obj = obj; }
var objPos = {
obj: obj,
x: origin.x,
y: parseInt(window.getComputedStyle(origin.circle).top, 10) - parseInt(window.getComputedStyle(obj).width, 10) / 2,
};
obj.style.position = "absolute";
obj.style.top = objPos.y + "px";
obj.style.left = objPos.x + "px";
return objPos;
}
function rotateObj(pointX, pointY, originX, originY, angle) {
var angle = angle * Math.PI / 180.0;
return {
x: Math.cos(angle) * (pointX-originX) - Math.sin(angle) * (pointY-originY) + originX,
y: Math.sin(angle) * (pointX-originX) + Math.cos(angle) * (pointY-originY) + originY,
};
}
var angle = 0;
var origin = markCircleCenter("circle", "origin");
var point = placeObjIntoOrbit("point");
function spin() {
if (angle >= 360) { angle = 0; }
angle += 2;
var newCoor = rotateObj(point.x, point.y, origin.x, origin.y, angle);
point.obj.style.left = newCoor.x + "px";
point.obj.style.top = newCoor.y + "px";
}
setInterval("spin('point')", 1000/30);
You are computing the center of the point but positioning it using its top-left point. You need to take the dimension of the point into account when you position it. Try changing the last two lines in function spin() to:
point.obj.style.left = (newCoor.x - 4) + "px";
point.obj.style.top = (newCoor.y - 4) + "px";
(You should get rid of the "- 4" and substitute half the width and height of the point, but this gets the idea across more tersely.)
The problem is that when I drag/drop a picture, it always gets put on the last letter.
Here's an image of what I am talking about:
Here is my code:
function mattes_draw_letter(x, y, width, height, letter, position)
{
var canvas = document.createElement('canvas');
canvas.style.position = "absolute";
canvas.style.top = 0 + "px";
canvas.id = "canvas_opening_" + position;
canvas.style.zIndex = 5;
var ctx = canvas.getContext("2d");
ctx.lineWidth = 1;
ctx.fillStyle = '#bfbfbf';
ctx.strokeStyle = '#000000';
ctx.beginPath();
ctx.moveTo(x + letter[0] * width, y + letter[1] * height);
for (i = 0; i < letter.length; i+=2)
{
if (typeof letter[i+3] !== 'undefined')
{
ctx.lineTo(x + letter[i+2] * width, y + letter[i+3] * height);
}
}
ctx.fill();
ctx.stroke();
ctx.closePath();
$("#mattes").append(canvas);
canvas.addEventListener("drop", function(event) {drop(event, this);}, false);
canvas.addEventListener("dragover", function(event) {allowDrop(event);}, false);
canvas.addEventListener("click", function() {photos_add_selected_fid(this);}, false);
}
Here is the code that has the for loop:
function mattes_create_openings(openings)
{
var opening_array = new Array();
var x = 0;
var y = 0;
var opening_width = 0;
var opening_height = 0;
$(openings).each(function(i, el) {
x = $(el).find("x").text() * ppi;
y = $(el).find("y").text() * ppi;
opening_width = $(el).find("width").text() * ppi;
opening_height = $(el).find("height").text() * ppi;
var type = $(el).find("type").text();
var text = $(el).find("text").text();
var ellipse = (type == "ellipse") ? " border-radius: 50% " : "";
if ("letter" == type)
{
var letter = mattes_get_letter_array(text);
var letter_width = (opening_width - (text.length * 0.5 * ppi)) / text.length;
var space = 0.5 * ppi;
var letterX = x;
var letterY = y;
var letter_height = opening_height;
mattes_draw_letter(letterX, letterY, letter_width, letter_height, letter, i);
letterX += (space + letter_width);
}
else
{
$("#mattes").append("<div id='opening_" + i + "' style='background-color: #bfbfbf; position: absolute; left: " + x + "px; top: " + y + "px; height: " + opening_height + "px; width: " + opening_width + "px; overflow: hidden; z-index: 4;" + ellipse + "' ondrop='drop(event, this)' ondragover='allowDrop(event)' onclick='photos_add_selected_fid(this);'> </div>");
}
//TODO multiple openings max/min -x,y,width, height
opening_array["x"] = x;
opening_array["y"] = y;
opening_array["opening_width"] = opening_width;
opening_array["opening_height"] = opening_height;
});
return opening_array;
}
You need to wrap your code inside $.each in a function call, like this:
$(openings).each(function(i, el) {
bindEvents(i, el);
};
function bindEvents(indx, ele) {
// Code what you have written inside $.each originally
};
The problem was not the event handlers, but the size of the canvas. The last canvas created was on top of the others so it was the only event that was being activated.
I have a dom editor which a user can insert textbox and images. One of my requirements involve saving a snapshot of what is in the editor into an image. I did some research and there are some solutions, but they don't seem 100% foolproof. I've tried implementing a solution myself, clobbering code here and there:
function measureText(text, size, font) {
var lDiv = document.createElement('lDiv');
document.body.appendChild(lDiv);
lDiv.style.fontSize = size;
lDiv.style.fontFamily = font;
lDiv.style.position = "absolute";
lDiv.style.left = -1000;
lDiv.style.top = -1000;
lDiv.innerHTML = text;
var metrics = font.measureText(text, size.slice(0, size.length - 2));
var lResult = {
width: lDiv.clientWidth,
height: metrics.height + lDiv.clientHeight
};
document.body.removeChild(lDiv);
lDiv = null;
return lResult;
}
function wrapText(context, item) {
var words = item.text.split(' ');
var line = '';
var x = parseInt(item.x);
var y = parseInt(item.y);
var width = parseInt(item.width.slice(0, item.width.length - 2));
var height = parseInt(item.height.slice(0, item.height.length - 2));
var fontsize = parseInt(item.size.slice(0, item.size.length - 2));
var font = new Font();
font.onload = function () {
context.save();
context.beginPath();
context.rect(x, y, width, height);
context.clip();
context.font = item.size + " " + item.font;
context.textBaseline = "top";
for (var n = 0; n < words.length; n++) {
var testLine = line + words[n] + ' ';
var metrics = measureText(testLine, item.size, font);
var testWidth = metrics.width;
if (testWidth > width && n > 0) {
console.log("Drawing '" + line + "' to " + x + " " + y);
context.fillText(line, x, y);
line = words[n] + ' ';
y += metrics.height
}
else {
line = testLine;
}
}
context.fillText(line, x, y);
context.restore();
}
font.fontFamily = item.font;
font.src = font.fontFamily;
}
this.toImage = function () {
console.log("testing");
var canvas = document.getElementById("testcanvas");
canvas.width = 400;
canvas.height = 400;
var ctx = canvas.getContext("2d");
var imageObj = new Image();
var thisService = this;
imageObj.onload = function () {
ctx.drawImage(imageObj, 0, 0, 400, 400);
for (var i = 0; i < thisService.canvasItems.length; i++) {
var component = thisService.canvasItems[i];
if (component.type == "textbox") {
var x = component.x.slice(0, component.x.length - 2);
var y = component.y.slice(0, component.y.length - 2);
var w = component.width.slice(0, component.width.length - 2);
var h = component.height.slice(0, component.height.length - 2);
wrapText(ctx, component);
}
}
};
imageObj.src = this.base.front_image;
}
Somehow I believe I almost made it, however from the ,
There seems to be some positioning/font placement issues, just a few pixels lower. The top 1 a div with no padding, (its model can be seem on the panel on the left), while the bottom one is the canvas.
I wish to have a 1 to 1 accurate mapping here, can anyone enlighten what might be the problem?
As far as I know its not possible to draw HTML into a canvas with 100% accuracy due to the obvious "security" reasons.
You can still get pretty close using the rasterizeHTML.js library.
It uses a SVG image containing the content you want to render. To draw HTML content, you'd use a element containing the HTML, then draw that SVG image into your canvas.