I'm trying to create a graph-to-png converter with phantomjs, but having a hard time getting it to work. Almost every example I see out there uses some external URL as if all you ever do with it is scraping, and the documentation is terribly lacking.
To test out things, I created a very simple d3 function that adds an <svg> tag and a blue circle inside. Looking at other questions on SO and examples, I hooked it to onLoadFinish but that event never triggers.
I'm guessing I need to open the page, but open only uses a url, which isn't relevant for me (and again, the docs are completely lacking in information. Even when I see something I think might be relevant, my only choice is guesswork. This is ridiculous )
Here's my code:
var page = require('webpage').create();
var content = '<html>';
content += '<body>';
content += '<h1> test title </h1>';
content += '<div id="graph">';
content += '</div>';
content += '</body></html>';
page.content = content;
page.injectJs('lib/d3/d3.min.js');
page.onLoadFinish = function(status) {
console.log('loading finished'); // this never happens!
var svg = d3.select('#graph')
.append('svg')
.attr({'width': 100, 'height': 100});
var circle = svg
.append('circle')
.attr({ 'cx': 10, 'cy': 10, 'r': 10, 'fill': 'blue' });
page.render('test.png');
phantom.exit();
};
Any suggestions?
It's evaluate. That was the function I was looking for. It "evaluates the given function in the context of the web page".
Finally, it's working:
page.content = content;
page.injectJs('lib/d3/d3.min.js');
page.evaluate(function() {
var svg = d3.select('#graph')
.append('svg')
.attr({'width': 100, 'height': 100});
var circle = svg
.append('circle')
.attr({ 'cx': 10, 'cy': 10, 'r': 10, 'fill': 'blue' });
page.render('test.png');
phantom.exit();
};
Related
This one I've been banging my head against for a few weeks now.
Scenario:
Let user generate an image out of layers they select
Convert image to canvas
Share image from canvas on facebook wall using share_open_graph (along with the image, a short text and title will be shared)
I've already had a solution in place using publish_actions but that was recently removed from the API and is no longer available.
I am using js and html for all code handling.
The issue is that I can generate a png image from the canvas but that is saved as base64 and share_open_graph doesn't allow this type of image, it needs a straight forward url such as './example.png'. I have tried using several approaches and with canvas2image, converting and saving image using file-system but all of these fail.
Does anyone have similar scenario and possible solution from April/May 2018 using share_open_graph ?
My current code looks like this - it fails at the image conversion and save to a file (Uncaught (in promise) TypeError: r.existsSync is not a function at n (file-system.js:30)). But I am open to different solutions as this is clearly not working.
html2canvas(original, { width: 1200, height: 628
}).then(function(canvas)
{
fb_image(canvas);
});
var fb_image = function(canvas) {
canvas.setAttribute('id', 'canvas-to-share');
document.getElementById('img-to-share').append(canvas);
fbGenerate.style.display = 'none';
fbPost.style.display = 'block';
var canvas = document.getElementById('canvas-to-share');
var data = canvas.toDataURL('image/png');
var encodedPng = data.substring(data.indexOf(',') + 1, data.length);
var decodedPng = base64.decode(encodedPng);
const buffer = new Buffer(data.split(/,\s*/)[1], 'base64');
pngToJpeg({ quality: 90 })(buffer).then(output =>
fs.writeFile('./image-to-fb.jpeg', output));
var infoText_content = document.createTextNode('Your image is being
posted to facebook...');
infoText.appendChild(infoText_content);
// Posting png from imageToShare to facebook
fbPost.addEventListener('click', function(eve) {
FB.ui(
{
method: 'share_open_graph',
action_type: 'og.shares',
href: 'https:example.com',
action_properties: JSON.stringify({
object: {
'og:url': 'https://example.com',
'og:title': 'My shared image',
'og:description': 'Hey I am sharing on fb!',
'og:image': './image-to-fb.jpeg',
},
}),
},
function(response) {
console.log(response);
}
);
});
};
I'm really into Google's Polymer and I love GSAP - and so far I've been using the two in conjunction without a hitch. Unfortunately I have now hit a problem - how do I use GSAP (TweenMax to be specific) with custom css variables?
For example:
To change someCssProperty of someElement I would
TweenMax.to(someElement, 1, someCssProperty: "value");
but the someCssProperty becomes an issue when I'm trying to animate a css variable, which take on the form --some-custom-css-variable .
I have tried to use
TweenMax.to(someElement, 1, --some-custom-css-Property: "value");
(obviously gives me errors) and I also tried to use TweenMax.to(someElement, 1, "--some-custom-css-Property": "value");
(quotes around the some-custom-Css-property) - however this results in
no change/animation and an invalid tween value error message on the developer console.
So the question is: how would I go about animating custom css variables with TweenMax (GSAP)?
Thanks for any help :)
EDIT:
I have tried using classes through
TweenMax.to("SomeElement", 5, {className:"class2"});
But this changed the element style as if I had declared it in css with a
SomeElement:hover {}
style (as in it does not animate, just changes immediately)
For now, you're going to have to manually update the variable using a generic object.
var docElement = document.documentElement;
var tl = new TimelineMax({ repeat: -1, yoyo: true, onUpdate: updateRoot });
var cs = getComputedStyle(docElement, null);
var blur = {
value: cs.getPropertyValue("--blur")
};
tl.to(blur, 2, { value: "25px" });
function updateRoot() {
docElement.style.setProperty("--blur", blur.value);
}
:root {
--blur: 0px;
}
img {
-webkit-filter: blur(var(--blur));
filter: blur(var(--blur));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.18.4/TweenMax.min.js"></script>
<img src="http://lorempixel.com/400/400/" />
I'm trying to find the size of the images being downloaded by a page; the obvious solution would be,
var imgs = [];
var imgTags = document.getElementsByTagName('img');
for (i = 0; i < imgTags.length; i++) {
var img = imgTags[i];
imgs.push({src: img.src, h: img.height, w: img.width});
}
but that will only retrieve the rendered size — for example, the ubiquitous spacer.gif will be 1x4, 4x4, etc., while it's obviously 1x1.
Moreover, that kind of document scanning doesn't seem to be able to pick the images that were downloaded, but not used in the img tags: CSS, backgrounds, etc.
The inspector in WebKit-based browsers seems to be pretty capable of rendering a preview — where does it take it from?
Hopefully, there two important properties for this : naturalWidth and naturalHeight (doc here)
Here is a sample script (first image of stackoverflow)
var page = require('webpage').create();
var url = 'http://www.stackoverflow.com/';
page.open(url, function(status) {
var first_image_dim = page.evaluate(function() {
return {width :$('img')[0].naturalWidth, height:$('img')[0].naturalHeight};
});
console.log(JSON.stringify(first_image_dim));
phantom.exit();
});
I have an image slider that is controlled by text navigation. The text is highlighted orange when it's relative slide is current in the gallery. I would like the other text to have an inactive state with a black background but cannot get this to work!
(In case that didn't make much sense! Basically, I want background-color orange when current, background-color black when inactive.) THANKS
$(document).ready(function(){
$('.slider').each(function(e){
if(e == 0){
$(this).addClass('current');
}
$(this).attr('id', 'handle' + e);
})
$('.tabs li').each(function(e){
if(e == 0){
$(this).addClass('current'); //adds class current to 1st li
}
$(this).wrapInner('<a class="title"></a>'); //wraps list items in anchor tag
$(this).children('a').attr('href', '#handle' + e);//adds href to the anchors
t = $(this).children('a').text();
$('#handle' + e).append('<h2>' + t + '</h2>'); //adds h2 and text to big images
})
$('.tabs li a').click(function(){
c = $(this).attr('href');
if($(c).hasClass('current')){
return false;
}else{
showImage($(c), 20);
$('.tabs li').removeClass('current');
$(this).parent().addClass('current');
return false;
}
})
runRotateImages();
$("#featured").hover(
function(){
clearTimeout(xx);
},
function(){
runRotateImages();
}
)
})
function showImage(img, duration){
$('.slider').removeClass('current').css({
"opacity" : 0.0,
"zIndex" : 2
});
img.animate({opacity:1.0}, duration, function(){
$(this).addClass('current').css({zIndex:1});
});
}
function rotateImages(){
var curPhoto = $("div.current");
var nxtPhoto = curPhoto.next();
var curTab = $(".tabs li.current");
var nxtTab = curTab.next();
if (nxtPhoto.length == 0) {
nxtPhoto = $('#featured div:first');
nxtTab = $('.tabs li:first-child');
}
curTab.removeClass('current');
nxtTab.addClass('current');
showImage(nxtPhoto, 300);
}
function runRotateImages(){
xx = setInterval("rotateImages()", 5000);
}
I have added a jsfiddle - http://jsfiddle.net/n5EPM/3/
However, on jsfiddle it does not seem to automatically cycle through the images, not sure why, have no problems in browser.
Try using not() method: http://api.jquery.com/not/
Basically, you need to create a new class disabled
.disabled{
background-color:#000000;
}
Then, add the following line to your tabs.li's each loop:
$(".tabs li").not(".current").addClass('disabled'); //add disabled class for non-current tabs
At last you need to remove disabled class in the rotateimage() function before assigning current and then disable non-current again. like this:
curTab.removeClass('current');
nxtTab.removeClass('disabled'); //remove diabled class
nxtTab.addClass('current');
$(".tabs li").not(".current").addClass('disabled'); // disable non-current again
Working jsfiddle here: http://jsfiddle.net/n5EPM/9/
This might not be the perfect solution but you will need to tweak it a little bit.
Hope this helps.
PhantomJS is doing a great job of capturing web pages into image files for me. I am using a script based on rasterize.js.
However, for certain web elements of a fixed size, I need the resulting image to match the size of the web element.
e.g. I have a page like this:
<html>
<body>
<div id="wrapper" style="width:600px; height:400px;">
Content goes here...
</div>
</body>
</html>
In this example, I need it to produce an image sized at 600x400. Is thre a way to get the viewport size dynamically based on a web element in the page I am rasterizing.
I know this one is not a easy one... Ideas?
Thanks
Wow. Not that hard after all. Just set the viewport really big and use the cropRect function. What a great tool!
Mods to rasterize.js:
page.open(address, function (status) {
if (status !== 'success') {
console.log('Unable to load the address!');
} else {
var height = page.evaluate(function(){
return document.getElementById('wrapper').offsetHeight;
});
var width = page.evaluate(function(){
return document.getElementById('wrapper').offsetWidth;
});
console.log('Crop to: '+width+"x"+height);
page.clipRect = { top: 0, left: 0, width: width, height: height };
window.setTimeout(function () {
page.render(output);
phantom.exit();
}, 200);
}
});