How can I use and transform a canvas behind an svg mask in a cross-browser way? - html

I've been doing a lot of research into Canvas and SVG, as well as CSS3 3d transforms, and I want to use them all together, and I'm having difficulty getting it all working. I've got an SVG element containing an image-based mask as well as a foreignObject element containing some HTML I'd like to mask, particularly, a canvas. Depending on what I do with the canvas, I'm getting different results in different browsers, and usually not what I want. Applying typical canvas operations, such as rect(), and applying typical css transformations, such as rotate3d(), will, in most browsers, cause the SVG image mask to no longer be applied to the canvas, and the entire canvas will be visible, including the masked part.
Safari (desktop): If the canvas has ever been drawn on or transformed in any way (even, for example, rotate3d(1,1,0,0deg)), it will not be masked.
Safari (iOS 7): If the canvas has ever been drawn on or transformed in any way, it will not be masked.
Chrome (desktop): Here, we can draw to the canvas and it will still be masked, but if it has been transformed, it will not be masked.
Chrome (iOS 7): If the canvas has been drawn on or transformed at all, it will not be masked.
Firefox: No problems here; it will still be masked even if it has been drawn on and transformed.
IE and others: I haven't checked yet.
You can see this illustrated by applying the following code, which is also hosted here, where I set up an example page: http://kage23.com/masktest.html
<svg width="748" height="421" baseProfile="full" version="1.2">
<defs>
<mask id="myMask" transform="scale(1)">
<image width="100%" height="421" xlink:href="http://i.imgur.com/ORtP2fW.png" />
</mask>
</defs>
<foreignObject id="foreignObject" width="100%" height="421">
<div style="background-color: green; width: 100%; height: 421px;">
<p id="myParagraph" style="padding-top: 100px; padding-left: 200px;">Some text or whatever.</p>
<canvas id="effectCanvas" width="500px" height="100px" style="background-color: red;"></canvas>
</div>
</foreignObject>
</svg>
<button onclick="applyMask()">Apply mask</button>
<button onClick="drawToCanvas()">Draw to canvas</button>
<button onClick="rotateCanvas()">Apply rotation to canvas</button>
<script>
var applyMask = function ()
{
var element = document.getElementById('foreignObject');
element.style.mask = 'url(#myMask)';
};
var drawToCanvas = function ()
{
var canvas = document.getElementById('effectCanvas');
var ctx = canvas.getContext('2d');
ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect (10, 10, 55, 50);
};
var rotateCanvas = function ()
{
var canvas = document.getElementById('effectCanvas');
canvas.style.webkitTransform = 'rotate3d(1,1,1,45deg)';
canvas.style.MozTransform = 'rotate3d(1,1,1,45deg)';
};
</script>
Is there a better way to do this? I'm not committed to using an SVG mask necessarily; I just need to be able to mask a transforming canvas that is also being actively drawn on. Originally, I was using -webkit-mask-box-image, but that's not supported very widely at all.

Related

Adding text onto an svg canvas

I'm having issues on adding text onto an svg canvas as it doesn't seem like the text is appearing in the canvas after many attempts.
I'm working on adding the number 0 on both left and right segment at the upper segment of the canvas but it doesn't seem to appear.
function createCounter() {
let scoreLeft: number = 0;
let scoreRight: number = 0;
const svg = document.getElementById("canvas")!,
counter = new Elem(svg, 'text')
.attr('text',scoreLeft)
.attr('x',250).attr('y', 400)
.attr('font', 'Arial')
.attr('font-size',32)
.attr('fill','#FFFFFF')
}
The function Elem is basically creating a new svg element, such as shapes and so on but I'm having issue with the text element as it doesn't seemed to appear. Am i missing out on any svg text attributes that causes it to not appear?
here is an example how to do text into svg
<svg height="30" width="200">
<text x="0" y="15" fill="red">I love SVG!</text>
Sorry, your browser does not support inline SVG.
</svg>

MxGraph: Is it possible to render a graph in HTML without SVG?

I'm looking for a way to render a graph in HTML, solely using mxGraph Javascript, without the use of an SVG canvas. The user manual says:
mxGraph also includes the feature to render entirely using html, this limits the range of functionality available, but is suitable for more simple diagrams."
However, I've tried the following without success:
var editor = new mxEditor();
var graph = new mxGraph(graphContElem, new mxGraphModel(), 'fastest'); // fastest maps to stricthtml
graph.setHtmlLabels(true);
graph.dialect = mxConstants.DIALECT_STRICTHTML;
editor.graph = graph;
editor.createGraph();
Adding this cell:
var prototype = new mxCell('<input type="text" value="test" />', new mxGeometry(0, 0, w, h), style);
prototype.setVertex(true);
... import cells ...
Leads to this:
<svg style="width: 100%; height: 100%; display: block; min-width: 1px; min-height: 1px;">
...
<g transform="translate(104,61)">
<foreignObject style="overflow:visible;" pointer-events="all" width="173" height="19">
<div style="display:inline-block;font-size:11px;font-family:Arial,Helvetica;color:#774400;line-height:1.2;vertical-align:top;white-space:nowrap;text-align:center;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display:inline-block;text-align:inherit;text-decoration:inherit;">
<input type="text" value="test">
</div>
</div>
</foreignObject>
</g>
...
</svg>
My goal is the development of a form editor; therefore the cells of the graph would be HTML widgets or web components, as, for example, input fields.
Is this possible with mxGraph? Thanks in advance!
The documentation needs updating, mxGraph hasn't supported that mode for a long time.

TweenMax.js transform rotate doesn't work in IE 9

I am trying to animate few Svg elements( Windmill , Door Open) using Tweenmax.js
The Windmill rotation works fine but the door opening animation doesn't work at all in Internet Explorer 9.
Here's my code :
var dooropen = $('#door-open');
var windmill = $('#windmill');
function DoorOpen() {
TweenMax.to(dooropen, 3, {
rotationY: 180,
transformOrigin: "0% 0%"
});
}
function rotateFan() {
TweenMax.to(windmill, 40, {
rotation: 360,
transformOrigin: "51% 64%"
});
}
$('.run').click(function() {
DoorOpen();
rotateFan();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/gsap/1.18.0/TweenMax.min.js"></script>
<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" width="887.996" height="567.947">
<path fill="#231F20" d="M89.917 99.025h31.58v468.42h-31.58z" />
<g id="windmill" fill="#FFF" stroke="#231F20" stroke-miterlimit="10">
<path d="M105.706 116.57s-50.877-56.142 0-115.79c0 0 47.368 57.894 0 115.79zM105.707 116.57s73.15-19.732 103.088 52.726c0 0-73.114 15.808-103.088-52.727z" />
<path d="M105.707 116.57S76.25 186.372.71 165.385c0 0 32.528-67.36 104.997-48.817z" />
</g>
<path fill="#FFF" stroke="#231F20" stroke-miterlimit="10" d="M485.496 162.376h402v402h-402z" />
<path fill="#231F20" d="M567.496 266.376h246v298h-246z" />
<g id="door-open">
<path fill="#B51543" d="M567.496 266.376h246v298h-246z" />
<path fill="#841C3F" d="M791.496 419.376c0 2.762-2.238 5-5 5h-26c-2.762 0-5-2.238-5-5v-26c0-2.762 2.238-5 5-5h26c2.762 0 5 2.238 5 5v26z" />
</g>
</svg>
Run
It looks like your issue is that your mixing string values with a numerical ones. The default transform-origin are percentages 50% 50% 0. So also it is best to stick with percentage based or numerical values for transform-origin in GSAP.
A two-value syntax is preferred like #Tahir Ahmed commented above. Even though the third parameter is accepted. The third value will automatically default to zero anyway, since that is the default value from the spec 0. And is only necessary when using 3D transforms, since it aligns itself to the z-axis. Which you cant use in SVG, since SVG does not support CSS 3D Transforms.
Have a look at the transform-origin spec:
https://developer.mozilla.org/en-US/docs/Web/CSS/transform-origin
Try this:
var dooropen = $('#door-open');
var windmill = $('#windmill');
function DoorOpen() {
TweenMax.to(dooropen, 3, {
rotationY: 180,
transformOrigin: "0% 0%" /* top and left, omit 0 since is the default */
});
}
function rotateFan() {
TweenMax.to(windmill, 40, {
rotation: 360,
transformOrigin: "51% 64%"
});
}
$('.run').click(function() {
DoorOpen();
rotateFan();
});
tranform-origin keyword equivalents:
left = 0%
center = 50%
right = 100%
top = 0%
bottom = 100%
Also check out the CSSPlugin Docs and the use of SVG transformOrigin
http://greensock.com/docs/#/HTML5/GSAP/Plugins/CSSPlugin/
GSAP svgOrigin property, taken from CSSPlugin Docs:
[Only for SVG elements] Works exactly like transformOrigin but it uses the SVG's global coordinate space instead of the element's local coordinate space. This can be very useful if, for example, you want to make a bunch of SVG elements rotate around a common point. You can either define an svgOrigin or a transformOrigin, not both (for obvious reasons).
See codepen example here of svgOrigin.
So you can do TweenLite.to(svgElement, 1, {rotation:270, svgOrigin:"250 100"}) if you'd like to rotate svgElement as though its origin is at x:250, y:100 in the SVG canvas's global coordinates. Units are not required. It also records the value in a data-svg-origin attribute so that it can be parsed back in. svgOrigin doesn't accommodate percentage-based values.
If you're trying to animate the rotationY of an SVG element, that's the problem - the SVG spec does NOT allow 3D at all. That's not a GSAP problem or limitation - it's literally the SVG spec. Some webkit browsers do technically recognize 3D CSS transforms, but that's non-standard and it definitely isn't supported in IE because IE ignores all CSS transforms on SVG elements. Transforms are supposed to be applied via the "transform" attribute which, again, does not support 3D.
My advice: don't do 3D in SVG. I wish I had better news for you.

Image size doesn't match the canvas size

I made canvas like this ,
<canvas id="mainCanvas" style="width:310px;height:212px;">
</canvas>
then try to put png on the canvas in my script
var canvas=document.getElementById("mainCanvas");
var context = canvas.getContext("2d");
var img= new Image();
img.src = "img/player.png";
context.drawImage(img,0,0,310,212);
my plyer.png size is 312 *212 , the same size as canvas size.
However,my png file is expansioned on the canvas. Consequently, right edge and bottom edge protrude from the canvas.
Why my png doesn't fit in ??
All canvas should have width and height attributes specified
<canvas width="310" height="212"></canvas>
Width and height shouldn't use 'px'.
With javascript use:
var c = document.querySelector('canvas');
c.width=310;
c.height=212;// as Ken Fyrstenberg mentioned
Also be sure to wait till image will load.
img.onload(function(){
//drawimage
});
You need to apply the size as an attribute not styles. Try this:
<canvas id="mainCanvas" width="310" height="212">
</canvas>

SVG mousemove events stop firing in Firefox after a few mousedowns

I am writing an HTML5 app using primarily SVG content, within an HTML page. One of the tasks I need to do is move objects around on the canvas. I find that, in Firefox, the first press-drag-release operation works as expected, but very soon the code stops receiving mousemove events after a mousedown. Instead, I see the "ghostbusters" cursor, as if Firefox thinks I am trying to do a Drag-and-Drop operation. I can eventually restore mousemove after mousedown events for one or two cycles by clicking around the green rectangle, but then the problem recurs. I have even set draggable="false" as seen in the simple test case below, to make sure DnD is disabled. (All of the content was copied from one file, though it looks like it is getting separated here.)
This test case works fine in IE9, Chrome, Opera, and Safari.
A similar "pure" SVG page works in Firefox as well as the other browsers.
I am using Firefox 15.0.1 on a Windows 7 client, serving the page up from a Linux box.
Is there something I am missing? Or is this perhaps a Firefox bug?
<!DOCTYPE HTML5>
<title>Test Mouse Events</title>
<script>
function mouseDown(evt)
{
document.getElementById("udText").textContent = "mousedown at "+evt.clientX+","+evt.clientY;
}
function mouseMove(evt)
{
document.getElementById("moveText").textContent = "mousemove at "+evt.clientX+","+evt.clientY;
}
function mouseUp(evt)
{
document.getElementById("udText").textContent = "mouseup at "+evt.clientX+","+evt.clientY;
}
function init()
{
document.getElementById("field").addEventListener("mousedown", mouseDown, false);
document.getElementById("field").addEventListener("mouseup", mouseUp, false);
document.getElementById("field").addEventListener("mousemove", mouseMove, false);
}
</script>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:ev="http://www.w3.org/2001/xml-events"
id="canvas"
width="800"
height="600"
version="1.1"
baseProfile="full"
onload="init()"
>
<rect id="field" x="50" y="50" width="700" height="500" fill="#20c000" draggable="false"/>
<text id="udText" x="80" y="520" font-size="30" fill="#000000">No up or down events yet</text>
<text id="moveText" x="420" y="520" font-size="30" fill="#000000">No move events yet</text>
</svg>
You should call evt.preventDefault(); in all the mouseXXX handlers. Firefox has default drag/drop handling and you don't want that. It's not a bug in Firefox that it does that.
These changes fix it for me:
function mouseDown(evt)
{
evt.preventDefault();
document.getElementById("udText").textContent = "mousedown at "+evt.clientX+","+evt.clientY;
}
function mouseMove(evt)
{
evt.preventDefault();
document.getElementById("moveText").textContent = "mousemove at "+evt.clientX+","+evt.clientY;
}
function mouseUp(evt)
{
evt.preventDefault();
document.getElementById("udText").textContent = "mouseup at "+evt.clientX+","+evt.clientY;
}