How to stop SVG text hover from stopping gradient fill - javascript
I want the svg background fill gradient to remain filled even hovering over text but when you hover over the text clears the fill.
I have tried moving the text around and even moving the "flag" class but no luck. Many thanks.
I have included the codepen is here: https://codepen.io/daneli84/pen/OJVZmeJ
HTML
<svg viewBox="0 0 100 100">
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:rgb(5,255,0);stop-opacity:1" />
<stop offset="100%" style="stop-color:rgb(255,0,0);stop-opacity:1" />
</linearGradient>
<linearGradient id="grad2" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:rgb(255,255,0);stop-opacity:1" />
<stop offset="100%" style="stop-color:rgb(20,0,0);stop-opacity:0.1" />
</linearGradient>
<filter x="-50%" y="-50%" width="200%" height="200%" filterUnits="objectBoundingBox"
id="filter-1">
<feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter1"></feOffset>
<feGaussianBlur stdDeviation="1" class="flag-blur" in="shadowOffsetOuter1" result="shadowBlurOuter1">
</feGaussianBlur>
<feColorMatrix values="0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 9 2 0" in="shadowBlurOuter1"
type="matrix" result="shadowMatrixOuter1"></feColorMatrix>
<feMerge>
<feMergeNode in="shadowMatrixOuter1"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<g id="pod">
<polygon stroke="#000000" stroke-width="1" points="5,-9 -5,-9 -10,0 -5,9 5,9 10,0" />
</g>
</defs>
<g class="pod-wrap">
<g transform="translate(50, 41)" class="flag">
<use xlink:href="#pod" class="h1 flag">
</use>
<text class="h1" alignment-baseline="middle" text-anchor="middle" font-family="Verdana" font-size="5"
fill="blue">CNI</text>
</g>
<use xlink:href="#pod" transform="translate(35, 50)" class="flag h2" />
<use xlink:href="#pod" transform="translate(65, 50)" class=" h3" />
<use xlink:href="#pod" transform="translate(50, 59)" class=" h4" />
</g>
</svg>
CSS
/* grid styling */
use {
cursor: pointer;
fill:transparent;
}
g {cursor: pointer;}
filter: url(#filter-1);
fill: url(#grad2);
}
/* other styling */
svg {
width: 700px;
flex: 1;
}
body {
di
splay: flex;
justify-content: center;
align-items: center;
flex-direction: column;
margin: 0;
height: 100vh;
font-weight: 700;
font-family: sans-serif;
}
JS
//
var flagBlur = document.querySelector('.flag-blur');
var flags = document.querySelectorAll('.flag');
//
function startPage() {
flags.forEach(flag => {
flag.onmouseover = function() {
flag.classList.add('filter-class')
TweenMax.fromTo(flagBlur, 1, {
attr: {
}
}, {
attr: {
stdDeviation: 1.2
},
ease: Power1.easeInOut
});
}
flag.onmouseleave = function() {
flag.classList.remove('filter-class')
}
})
}
startPage();
You can use the pointer-event attribute on your text element
Other options are: bounding-box | visiblePainted | visibleFill | visibleStroke | visible | painted | fill | stroke | all | none, but in this case, just use none
<text
class="h1"
alignment-baseline="middle"
text-anchor="middle"
font-family="Verdana"
font-size="5"
fill="blue"
pointer-events="none"
>CNI</text>
The pointer-events attribute is a presentation attribute that allows defining whether or when an element may be the target of a mouse event.
more reading: https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/pointer-events
Related
SVG-graphics: cloudy border
Is it possible to draw cloudy border for rect and polygon using svg-graphics? I tried using patterns in stroke, but it does not work. I need border like this Thank you!
You can fake it by using a dashed stroke with an SVG filter applied. Note this will likely slow down your page, so use the filter with caution. <svg viewBox="0 0 300 500" width="600" xmlns="http://www.w3.org/2000/svg"> <defs> <filter id="outline-sobel" x="-50%" y="-50%" width="200%" height="200%"> <feGaussianBlur in="SourceAlpha" stdDeviation="0.3" result="BLUR" /> <feConvolveMatrix in="BLUR" result="TOP-SOBEL" kernelMatrix=" 1 2 1 0 0 0 -1 -2 -1"/> <feConvolveMatrix in="BLUR" result="BOTTOM-SOBEL" kernelMatrix=" -1 -2 -1 0 0 0 1 2 1"/> <feConvolveMatrix in="BLUR" result="LEFT-SOBEL" kernelMatrix=" 1 0 -1 2 0 -2 1 0 -1"/> <feConvolveMatrix in="BLUR" result="RIGHT-SOBEL" kernelMatrix=" -1 0 1 -2 0 2 -1 0 1"/> <feComposite in="LEFT-SOBEL" in2="RIGHT-SOBEL" result="X" /> <feComposite in="TOP-SOBEL" in2="BOTTOM-SOBEL" result="Y" /> <feComposite in="X" in2="Y" /> </filter> <style> .cb1{ fill: none; stroke: tomato; stroke-width: 10; } .cb2{ stroke-dasharray: 2 10; } .cb3{ stroke-linecap: round; stroke-linejoin: round; } .cb4{ fill: tomato; } .cb5 { stroke-dasharray: 1 10 .9 10 1.1 10 .8 10 .7 10; stroke-dashoffset: 4; filter: url(#outline-sobel); } .cb6 { stroke-width: 5; stroke-dasharray: 0 5 0.1 5 0 5 ; } </style> </defs> <rect x="10" y="10" width="80" height="30" class="cb1" /> <rect x="10" y="60" width="80" height="30" class="cb1 cb2" /> <rect x="110" y="10" width="80" height="30" class="cb1 cb2 cb3" /> <rect x="110" y="60" width="80" height="30" class="cb1 cb2 cb3 cb4" /> <rect x="210" y="10" width="80" height="30" class="cb1 cb2 cb3 cb4 cb5" /> <path d=" M210 90 h80 a 10 10 0 0 0 -12 -20 a 18 18 0 0 0 -41 4 a 13 13 0 0 0 -26 14 z" class="cb1 cb2 cb3 cb4 cb5 cb6" /> </svg> Play with the values on the stroke-dasharray to get different effects, I've deliberately given a slightly random look.
svg to canvas image in jspdf - I Get a black content
I've used jspdf to convert the svg image to canvas and then to pdf The plugins I've used are <script type="text/javascript" src="assets/js/jquery.js"></script> <script type="text/javascript" src="assets/js/jspdf.js"></script> <script type="text/javascript" src="assets/js/html2canvas.js"></script> <script type="text/javascript" src="assets/js/html2canvas.svg.js"></script> <script type="text/javascript" src="assets/js/canvg.js"></script> My HTML code for the svg is genpdf <svg class="bullet" width="416" height="50"> <g transform="translate(75,5)"> <rect class="range s0" width="301" height="25" x="0"/> <rect class="range s1" width="250.83333333333334" height="25" x="0"/> <rect class="range s2" width="200.66666666666666" height="25" x="0"/> <rect class="measure s0" width="230.76666666666668" height="8.333333333333334" x="0" y="8.333333333333334"/> <rect class="measure s1" width="210.7" height="8.333333333333334" x="0" y="8.333333333333334"/> <g class="tick" transform="translate(0,0)" style="opacity: 1;"> <line y1="25" y2="29.166666666666668"/> <text text-anchor="middle" dy="1em" y="29.166666666666668">0</text> </g> <g class="tick" transform="translate(50.16666793823242,0)" style="opacity: 1;"> <line y1="25" y2="29.166666666666668"/> <text text-anchor="middle" dy="1em" y="29.166666666666668">5</text> </g> <g class="tick" transform="translate(100.33333587646484,0)" style="opacity: 1;"> <line y1="25" y2="29.166666666666668"/> <text text-anchor="middle" dy="1em" y="29.166666666666668">10</text> </g> <g class="tick" transform="translate(150.5,0)" style="opacity: 1;"> <line y1="25" y2="29.166666666666668"/> <text text-anchor="middle" dy="1em" y="29.166666666666668">15</text> </g> <g class="tick" transform="translate(200.6666717529297,0)" style="opacity: 1;"> <line y1="25" y2="29.166666666666668"/> <text text-anchor="middle" dy="1em" y="29.166666666666668">20</text> </g> <g class="tick" transform="translate(250.8333282470703,0)" style="opacity: 1;"> <line y1="25" y2="29.166666666666668"/> <text text-anchor="middle" dy="1em" y="29.166666666666668">25</text> </g> <g class="tick" transform="translate(301,0)" style="opacity: 1;"> <line y1="25" y2="29.166666666666668"/> <text text-anchor="middle" dy="1em" y="29.166666666666668">30</text> </g> <g x="200" y="100" style="text-anchor: end;" transform="translate(-6,12.5)"> <text class="title">NOVORA</text> </g> </g> </svg> I'm unable to get the exact image that is required as an output when I generate it to pdf. I'm converting the svg to canvas but still I'm unable to get the desired output. Instead of the exact image I get a black content inside that. The css code for the above one is .bullet { font: 10 px sans-serif; } .bullet.marker { stroke: #000; stroke-width: 2px; } .bullet .tick line { stroke: # 666; stroke-width: .5px; } .bullet.range.s0 { fill: #eee; } .bullet.range.s1 { fill: #ddd; } .bullet.range.s2 { fill: #ccc; } .bullet.measure.s0 { fill: steelblue; } .bullet.measure.s1 { fill: #fff; } .bullet.measure.s2 { fill: steelblue; } .bullet.measure.s3 { fill: lightsteelblue; } .bullet.title { font-size: 10px; font-weight: bold; } .bullet.subtitle { fill: #999; } The javascript that helps me rendering the data is function genPdf(){ var svgElements = $("#page-content-wrapper").find('svg'); //replace all svgs with a temp canvas svgElements.each(function() { var canvas, xml; canvas = document.createElement("canvas"); canvas.className = "screenShotTempCanvas"; // canvas.getContext("3d"); //convert SVG into a XML string xml = (new XMLSerializer()).serializeToString(this); // Removing the name space as IE throws an error xml = xml.replace(/xmlns=\"http:\/\/www\.w3\.org\/2000\/svg\"/, ''); //draw the SVG onto a canvas canvg(canvas, xml); $(canvas).insertAfter(this); $(this).addClass('tempHide'); $(this).hide(); }); html2canvas(document.getElementById('page-content-wrapper'), { onrendered: function (canvas){ var img = canvas.toDataURL("image/jpeg",1.0); var doc = new jsPDF(); doc.addImage(img, 'JPEG' , 10, 10); doc.save('test.pdf'); } }); setTimeout(function(){ $("#page-content-wrapper").find('.screenShotTempCanvas').remove(); $("#page-content-wrapper").find('.tempHide').show().removeClass('tempHide'); $("#page-content-wrapper").find('.bullet').show(); },2000); } function getStyle(el, styleProp) { var camelize = function(str) { return str.replace(/\-(\w)/g, function(str, letter) { return letter.toUpperCase(); }); }; if (el.currentStyle) { return el.currentStyle[camelize(styleProp)]; } else if (document.defaultView && document.defaultView.getComputedStyle) { return document.defaultView.getComputedStyle(el, null) .getPropertyValue(styleProp); } else { return el.style[camelize(styleProp)]; } } Please help me out in this. Thanks
How to place svg inside the div
I try to create multiple svg tag inside of the div, I want all the svg exist only inside of the parent div tag, if the coordinate of the svg is larger than the size of the div, I want the div become a scroll bar to expand the space. I also want to place the svg in the position I want. I am not sure how to make it happen? Thank you very much. $(document).ready(function() { $('#testbtm').click(function() { var svgElement = $('<svg class="hexagon" class="ui-widget-content">\ <text fill="black" x=75 y=75 style="text-anchor: middle">1</text>\ <path d="M38 0 L113 0 L150 65 L113 130 L38 130 L0 65 Z" / fill="none" stroke="blue"></svg>'); svgElement.children('text').text(1); svgElement.attr("class", "hexagon ui-widget-content"); $("#display").append(svgElement); svgElement.click(hexagonClick); }); //end click $('#testbtm2').click(function() { $('.hexagon').each(function() { var svgElement = $('<svg class="hexagon" class="ui-widget- content">\ <text fill="black" x=75 y=75 style="text-anchor: middle">1</text>\ <path d="M38 0 L113 0 L150 65 L113 130 L38 130 L0 65 Z" / fill="none" stroke="blue"></svg>'); svgElement.children('text').text(1); svgElement.attr("class", "hexagon ui-widget-content"); $("#display").append(svgElement); svgElement.click(hexagonClick); }); }); // end click $('.hexagon').click(hexagonClick); // end click }); // end ready #display { height: 500px; width: 500px; border: 1px solid black; } <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> <div id="display"> </div> <button id="testbtm">test</button> <button id="testbtm2">test2</button>
You can use overflow: auto; $(document).ready(function() { $('#testbtm').click(function() { var svgElement = $('<svg class="hexagon" class="ui-widget-content">\ <text fill="black" x=75 y=75 style="text-anchor: middle">1</text>\ <path d="M38 0 L113 0 L150 65 L113 130 L38 130 L0 65 Z" / fill="none" stroke="blue"></svg>'); svgElement.children('text').text(1); svgElement.attr("class", "hexagon ui-widget-content"); $("#display").append(svgElement); svgElement.click(hexagonClick); }); //end click $('#testbtm2').click(function() { $('.hexagon').each(function() { var svgElement = $('<svg class="hexagon" class="ui-widget- content">\ <text fill="black" x=75 y=75 style="text-anchor: middle">1</text>\ <path d="M38 0 L113 0 L150 65 L113 130 L38 130 L0 65 Z" / fill="none" stroke="blue"></svg>'); svgElement.children('text').text(1); svgElement.attr("class", "hexagon ui-widget-content"); $("#display").append(svgElement); svgElement.click(hexagonClick); }); }); // end click $('.hexagon').click(hexagonClick); // end click }); // end ready #display { height: 500px; width: 500px; border: 1px solid black; overflow: auto; } <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script> <div id="display"> </div> <button id="testbtm">test</button> <button id="testbtm2">test2</button>
Is there any way to colorize only part of image on hover?
I would love to code simple image painting in HTML, CSS and probably jQuery also. Let's say I have a original image, and I want make it colorized but only in part of hover (or 10x10px square or circle of image where cursor is). I applied some filters to make it grayscale with CSS, but I have no idea how to colorize only hover part (not whole picture). Example image of best result (keeping colorized advice would be great, but not necessarily).
You could do this using svg's mask and filter. CodePen var img = document.getElementById('img'); img.addEventListener('mousemove', function(e) { document.getElementById('c').setAttribute('cx', e.clientX - img.getBoundingClientRect().left); document.getElementById('c').setAttribute('cy', e.clientY - img.getBoundingClientRect().top); }) <svg id="img" width="600" height="300" viewBox="0 0 600 300"> <defs> <filter id="f" filterUnits="userSpaceOnUse"> <feColorMatrix type="saturate" values="0" /> </filter> <mask id="m" maskUnits="userSpaceOnUse" x="0" y="0" width="600" height="300"> <circle id="c" cx="-40" cy="-40" r="40" fill="white" /> </mask> </defs> <image filter="url(#f)" width="600" height="300" xlink:href="http://www.lorempixel.com/600/300" /> <image mask="url(#m)" width="600" height="300" xlink:href="http://www.lorempixel.com/600/300" /> </svg> You could also get a smooth transition on the circle edges by using radialGradient. CodePen var img = document.getElementById('img'); img.addEventListener('mousemove', function(e) { var x = e.clientX - img.getBoundingClientRect().left; var y = e.clientY - img.getBoundingClientRect().top; document.getElementById('r').setAttribute('fx', x); document.getElementById('r').setAttribute('fy', y); document.getElementById('r').setAttribute('cx', x); document.getElementById('r').setAttribute('cy', y); }); <svg id="img" width="600" height="300" viewBox="0 0 600 300"> <defs> <radialGradient id="r" gradientUnits="userSpaceOnUse" cx="300" cy="150" r="400" fx="300" fy="150"> <stop offset="0%" stop-color="white" /> <stop offset="10%" stop-color="white" /> <stop offset="12%" stop-color="black" /> <stop offset="100%" stop-color="black" /> </radialGradient> <filter id="f" filterUnits="userSpaceOnUse"> <feColorMatrix type="saturate" values="0" /> </filter> <mask id="m" maskUnits="userSpaceOnUse" x="0" y="0" width="600" height="300"> <path d="M0,0 h600 v300 h-600z" fill="url(#r)" /> </mask> </defs> <image filter="url(#f)" width="600" height="300" xlink:href="http://www.lorempixel.com/600/300" /> <image mask="url(#m)" width="600" height="300" xlink:href="http://www.lorempixel.com/600/300" /> </svg>
I suggest avoiding CSS filters, as it is not supported in IE at all, and doesn't look like it is in the pipeline either. I also would prefer to greyscale my images in photoshop, to have more control over the color balance and contrast. (But I'm a designer as well). Instead, I'm going to layer a full color image over a grayscale image, fix the position of the colorful background image, and move the position of the top div with jQuery: HTML <div class="greykitty"> <div class="colorfulkitty" style="top: 150px; left: 280px;"> </div> </div> SCSS with normalize.css body{ background-color:whitesmoke; } div{ height: 400px; width: 600px; background-repeat: no-repeat; } .greykitty{ background-image: url("http://lorempixel.com/g/600/400/cats/10/"); } .colorfulkitty{ background-image: url("http://lorempixel.com/600/400/cats/10/"); $circlesize: 150px; height: $circlesize; width: $circlesize; border-radius: $circlesize; background-attachment: fixed; position: absolute; } JS with jQuery $('.greykitty').mousemove(function (colorize) { var X = colorize.clientX; var Y = colorize.clientY; $('.colorfulkitty').css("top", (Y - 75) + 'px'); $('.colorfulkitty').css("left", (X - 75) + 'px'); }); And my codepen: http://codepen.io/fontophilic/pen/XJpVje/
You can wrap you image in a HTML Element and add a div element element with box-shadow $("figure").on('mousemove', function(e){ $('.shadow').css({ left: e.pageX - $(this).offset().left - 40, top: e.pageY - $(this).offset().top -40 }); }); figure{ position: relative; margin: 20px auto; width: 480px; height: 480px; overflow: hidden } figure:hover .shadow{ opacity: 1 } img{ width: 100% } .shadow{ position: absolute; left: 80px; top: 60px; z-index: 1; background: transparent; width: 100px; height: 100px; opacity: 0; transition: opacity .3s ease; border-radius: 50%; box-shadow: 0 0 0 60em rgba(0,0,0,.5) } <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <figure> <img src=http://i.imgur.com/orn8Dgf.jpg /> <div class=shadow></div> </figure>
Base on this, i have solution for your problem: Use mark to overlay image <div class="container">` <div class="bg-image"></div> <div class="highlight-region"></div> </div> Grayscale on mark instead of image's container .container .bg-image { opacity:0.3; -moz-filter: url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#grayscale"); -o-filter: url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#grayscale"); -webkit-filter: grayscale(100%); filter: gray; filter: url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\'><filter id=\'grayscale\'><feColorMatrix type=\'matrix\' values=\'0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0.3333 0.3333 0.3333 0 0 0 0 0 1 0\'/></filter></svg>#grayscale"); height:455px; width:606px; } set opacity = 0 on highlight-region .container div.highlight-region { height:150px; width:150px; border-radius: 50%; opacity:0; } Demo can see here: http://jsfiddle.net/MT4T7/438/
How can I get the currently-rendered (SVG) DOM when using Angular?
I have put together a simple HTML page using Angular which can update an SVG drawing. This all works nicely, and with little effort. However, I want to be able to render the SVG to a PNG file in-browser for easy download and reuse. The SVG drawing is set up like this: <svg viewBox="0 0 512 512" width="512" height="512" id="svg"> <linearGradient id="background-gradient" x1="50%" y1="0%" x2="50%" y2="100%"> <stop offset="0%" stop-color="{{background.start}}"/> <stop offset="100%" stop-color="{{background.end}}"/> </linearGradient> <linearGradient id="primary-gradient" x1="50%" y1="0%" x2="50%" y2="100%"> <stop offset="0%" stop-color="{{primary.start}}"/> <stop offset="100%" stop-color="{{primary.end}}"/> </linearGradient> <linearGradient id="accent-gradient" x1="50%" y1="0%" x2="50%" y2="100%"> <stop offset="0%" stop-color="{{accent.start}}"/> <stop offset="100%" stop-color="{{accent.end}}"/> </linearGradient> <polygon points="256,0 478,128 478,384 256,512 34,384 34,128" fill="url(#background-gradient)"/> <path d="M256,256 m-128,0 a128,128 0 1,1 256,0 a128,128 0 1,1 -256,0 Z M208,224 m-28,0 a28,28 0 1,1 56,0 a28,28 0 1,1 -56,0 Z M304,224 m-28,0 a28,28 0 1,1 56,0 a28,28 0 1,1 -56,0 Z M256,304 m40,0 a40,40 0 1,1 -80,0 Z" fill-rule="evenodd" fill="url(#primary-gradient)"/> <path d="M216,224 m-16,0 a16,16 0 1,1 32,0 a16,16 0 1,1 -32,0 Z M296,224 m-16,0 a16,16 0 1,1 32,0 a16,16 0 1,1 -32,0 Z" fill-rule="evenodd" fill="url(#accent-gradient)"/> </svg> Note that the stop-colors in the gradient come from the Angular model. When rendering to a PNG, I create an Image with the SVG source, draw that onto a JavaScript-created <canvas>, and then convert the <canvas> contents to a data: URI. Unfortunately, this is where things break down. Using innerHTML on the SVG drawing leaves the Angular placeholders in the result, rather than replacing them as expected. This means that all of the gradients turn out to be fully black, as their color values are literally {{background.start}}, etc. Obviously this does not produce a good result ☺ So my question is this: How can I get the SVG DOM that's rendered for display, so I can create the PNG successfully? I have been testing this in Chrome v39 and v40 (with various minor versions) on Linux. Reproduce code: <!DOCTYPE html> <html ng-app="BadgeCreator"> <head> <title>Badge Creator</title> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.5/angular.min.js"></script> <script> var app = angular.module('BadgeCreator', []) .config( [ '$compileProvider', function( $compileProvider ) { $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|data):/); } ]); app.controller('BadgeController', ['$scope', function($scope) { var updateDownloadLink = function() { var ctx, mycanvas, svg_data, img, child, target = document.getElementById('svg'); // Construct an SVG image svg_data = '<svg xmlns="http://www.w3.org/2000/svg" width="' + target.offsetWidth + '" height="' + target.offsetHeight + '">' + target.innerHTML + '</svg>'; console.log(svg_data); img = new Image(); img.src = "data:image/svg+xml," + encodeURIComponent(svg_data); // Draw the SVG image to a canvas mycanvas = document.createElement('canvas'); mycanvas.width = target.offsetWidth; mycanvas.height = target.offsetHeight; ctx = mycanvas.getContext("2d"); ctx.drawImage(img, 0, 0); // Return the canvas's data $scope.downloadUrl = mycanvas.toDataURL("image/png"); }; $scope.background = {start: '#111', end:'#333'}; $scope.primary = {start: '#c96', end:'#963'}; $scope.accent = {start: '#3cf', end:'#39c'}; $scope.$watch('background', updateDownloadLink, true); $scope.$watch('primary', updateDownloadLink, true); $scope.$watch('accent', updateDownloadLink, true); }]); </script> <style> .checkerback { background-color: #fff; background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee), linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee); background-size:64px 64px; background-position:0 0, 32px 32px; border: 1px solid #ccc; } </style> </head> <body ng-controller="BadgeController"> <div style="width:512px; height:512px; margin: 0 auto;" class="checkerback"> <svg viewBox="0 0 512 512" width="512" height="512" id="svg"> <linearGradient id="background-gradient" x1="50%" y1="0%" x2="50%" y2="100%"> <stop offset="0%" stop-color="{{background.start}}"/> <stop offset="100%" stop-color="{{background.end}}"/> </linearGradient> <linearGradient id="primary-gradient" x1="50%" y1="0%" x2="50%" y2="100%"> <stop offset="0%" stop-color="{{primary.start}}"/> <stop offset="100%" stop-color="{{primary.end}}"/> </linearGradient> <linearGradient id="accent-gradient" x1="50%" y1="0%" x2="50%" y2="100%"> <stop offset="0%" stop-color="{{accent.start}}"/> <stop offset="100%" stop-color="{{accent.end}}"/> </linearGradient> <polygon points="256,0 478,128 478,384 256,512 34,384 34,128" fill="url(#background-gradient)"/> <path d="M256,256 m-128,0 a128,128 0 1,1 256,0 a128,128 0 1,1 -256,0 Z M208,224 m-28,0 a28,28 0 1,1 56,0 a28,28 0 1,1 -56,0 Z M304,224 m-28,0 a28,28 0 1,1 56,0 a28,28 0 1,1 -56,0 Z M256,304 m40,0 a40,40 0 1,1 -80,0 Z" fill-rule="evenodd" fill="url(#primary-gradient)"/> <path d="M216,224 m-16,0 a16,16 0 1,1 32,0 a16,16 0 1,1 -32,0 Z M296,224 m-16,0 a16,16 0 1,1 32,0 a16,16 0 1,1 -32,0 Z" fill-rule="evenodd" fill="url(#accent-gradient)"/> </svg> </div> <p style="text-align: right">Download image</p> <p>Background: <input ng-model="background.start">–<input ng-model="background.end"></p> <p>Primary: <input ng-model="primary.start">–<input ng-model="primary.end"></p> <p>Accent: <input ng-model="accent.start">–<input ng-model="accent.end"></p> </body> </html>
A couple of things, You can use the $interpolate to create a live template of your html, and then pass it the current scope to get the rendered html. before using the img to draw it on the canvas, you must wait until the image is loaded (use its onload event) get the <svg> elements width/height attributes to get the dimensions Refactored code var app = angular.module('BadgeCreator', []) .config([ '$compileProvider', function($compileProvider) { $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|data):/); } ]); app.controller('BadgeController', ['$scope', '$interpolate', function($scope, $interpolate) { var target = document.getElementById('svg'), ngElement = angular.element(target), svgExpression = $interpolate(ngElement.html()), updateDownloadLink = function(newval, oldval, scope) { var ctx, mycanvas, svg_data, img, child, liveHtml = svgExpression(scope), svgWidth = parseInt(target.getAttribute('width'), 10), svgHeight = parseInt(target.getAttribute('height'), 10), svg_data = '<svg xmlns="http://www.w3.org/2000/svg" width="' + svgWidth + '" height="' + svgHeight + '">' + liveHtml + '</svg>', img = new Image(); img.onload = function() { // Draw the SVG image to a canvas mycanvas = document.createElement('canvas'); mycanvas.width = svgWidth; mycanvas.height = svgHeight; ctx = mycanvas.getContext("2d"); ctx.drawImage(img, 0, 0); // Return the canvas's data scope.$apply(function() { scope.downloadUrl = mycanvas.toDataURL("image/png"); }); }; img.src = "data:image/svg+xml," + encodeURIComponent(svg_data); }; $scope.background = { start: '#111', end: '#333' }; $scope.primary = { start: '#c96', end: '#963' }; $scope.accent = { start: '#3cf', end: '#39c' }; $scope.$watch('background', updateDownloadLink, true); $scope.$watch('primary', updateDownloadLink, true); $scope.$watch('accent', updateDownloadLink, true); } ]); .checkerback { background-color: #fff; background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee), linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee); background-size: 64px 64px; background-position: 0 0, 32px 32px; border: 1px solid #ccc; } <!DOCTYPE html> <html ng-app="BadgeCreator"> <head> <title>Badge Creator</title> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.5/angular.min.js"></script> </head> <body ng-controller="BadgeController"> <div style="width:512px; height:512px; margin: 0 auto;" class="checkerback"> <svg viewBox="0 0 512 512" width="512" height="512" id="svg"> <linearGradient id="background-gradient" x1="50%" y1="0%" x2="50%" y2="100%"> <stop offset="0%" stop-color="{{background.start}}" /> <stop offset="100%" stop-color="{{background.end}}" /> </linearGradient> <linearGradient id="primary-gradient" x1="50%" y1="0%" x2="50%" y2="100%"> <stop offset="0%" stop-color="{{primary.start}}" /> <stop offset="100%" stop-color="{{primary.end}}" /> </linearGradient> <linearGradient id="accent-gradient" x1="50%" y1="0%" x2="50%" y2="100%"> <stop offset="0%" stop-color="{{accent.start}}" /> <stop offset="100%" stop-color="{{accent.end}}" /> </linearGradient> <polygon points="256,0 478,128 478,384 256,512 34,384 34,128" fill="url(#background-gradient)" /> <path d="M256,256 m-128,0 a128,128 0 1,1 256,0 a128,128 0 1,1 -256,0 Z M208,224 m-28,0 a28,28 0 1,1 56,0 a28,28 0 1,1 -56,0 Z M304,224 m-28,0 a28,28 0 1,1 56,0 a28,28 0 1,1 -56,0 Z M256,304 m40,0 a40,40 0 1,1 -80,0 Z" fill-rule="evenodd" fill="url(#primary-gradient)" /> <path d="M216,224 m-16,0 a16,16 0 1,1 32,0 a16,16 0 1,1 -32,0 Z M296,224 m-16,0 a16,16 0 1,1 32,0 a16,16 0 1,1 -32,0 Z" fill-rule="evenodd" fill="url(#accent-gradient)" /> </svg> </div> <p style="text-align: right">Download image </p> <p>Background: <input ng-model="background.start">– <input ng-model="background.end"> </p> <p>Primary: <input ng-model="primary.start">– <input ng-model="primary.end"> </p> <p>Accent: <input ng-model="accent.start">– <input ng-model="accent.end"> </p> </body> </html>