Posting a HTML5 Canvas image to Facebook and Twitter
Update #1 - June 8, 2019
Due to changes to their API, Canvas image sharing to Facebook is not possible at this time.
Part of a recent project was to take an HTML 5 Canvas Image and post it to Facebook and Twitter. Although this sounds pretty straightforward, the challenge was that there would be no image saved to the server. I couldn't just slap share buttons on the page, because share dialogs expect content that already exists on a server. Using Facebook's API and Twitter's API we are able to share rich dynamic content. Here's a guide on what I did... (PHP, Javascript, jQuery, TwitterOAuth)
Canvas Image
Lets create a simple canvas element and add an image of a panda (we all like pandas, right?).
HTML
1<canvas id="canvas" style="border:1px solid black;" width="256" height="256"></canvas>
JavaScript
1// Canvas Object
2var canvas = document.getElementById('canvas');
3var ctx = canvas.getContext('2d');
4
5// load image from data url
6var imageObj = new Image();
7imageObj.onload = function() {
8ctx.drawImage(this, 0, 0);
9};
10
11imageObj.src = 'panda_dark.png';
Resulting Canvas
Canvas to Base64 to Blob/Binary String to Social Media
In the following steps we will convert the canvas image to base64 by using .toDataURL() which is part of the Canvas API.
HTMLCanvasElement.toDataURL() Returns a data-URL containing a representation of the image in the format specified by the type parameter.
The function below will take the base64 string and convert it to a Blob/Binary String that will be sent to the Facebook and Twitter APIs. This function will be referenced later in the code.
Blob(blobParts[, options])
Returns a newly created
Blob
object whose content consists of the concatenation of the array of values given in parameter.
1function dataURItoBlob(dataURI) {
2 var byteString = atob(dataURI.split(',')[1]);
3 var ab = new ArrayBuffer(byteString.length);
4 var ia = new Uint8Array(ab);
5 for (var i = 0; i < byteString.length; i++) {
6 ia[i] = byteString.charCodeAt(i);
7 }
8 return new Blob([ab], {type: 'image/png'});
9}
Posting to Facebook
Posting the image to Facebook will require creating a Facebook app which you will also need to request the 'publish_actions' permission when making the app live. Now we create a 'Post to Facebook' button and link a click handler for the logic. Below you can see that we grab the base64 encoding of the canvas image that has been returned in the format of a .png
image. We then try to convert the base64 string into a Blob. Using the Facebook JS SDK, after the user has logged in and given the app permission, the 'postImageToFacebook'
function is called and passed several arguments.
1$('#shareFB').click(function () {
2 var data = $('#canvas')[0].toDataURL("image/png");
3 try {
4 blob = dataURItoBlob(data);
5 } catch (e) {
6 console.log(e);
7 }
8 FB.getLoginStatus(function (response) {
9 console.log(response);
10 if (response.status === "connected") {
11 postImageToFacebook(response.authResponse.accessToken, "Canvas to Facebook/Twitter", "image/png", blob, window.location.href);
12 } else if (response.status === "not_authorized") {
13 FB.login(function (response) {
14 postImageToFacebook(response.authResponse.accessToken, "Canvas to Facebook/Twitter", "image/png", blob, window.location.href);
15 }, {scope: "publish_actions"});
16 } else {
17 FB.login(function (response) {
18 postImageToFacebook(response.authResponse.accessToken, "Canvas to Facebook/Twitter", "image/png", blob, window.location.href);
19 }, {scope: "publish_actions"});
20 }
21 });
22});
Posting to the blob to facebook requires the use of FormData. The function first uploads the picture to facebook without creating a story on the user's timeline. Then it retrieves the saved image and creates a story on the user's timeline using the fields provided. NOTE: The 'message' field must be left blank unless you provide the user a way to add one.
1function postImageToFacebook(token, filename, mimeType, imageData, message) {
2 var fd = new FormData();
3 fd.append("access_token", token);
4 fd.append("source", imageData);
5 fd.append("no_story", true);
6
7 // Upload image to facebook without story(post to feed)
8 $.ajax({
9 url: "https://graph.facebook.com/me/photos?access_token=" + token,
10 type: "POST",
11 data: fd,
12 processData: false,
13 contentType: false,
14 cache: false,
15 success: function (data) {
16 console.log("success: ", data);
17
18 // Get image source url
19 FB.api(
20 "/" + data.id + "?fields=images",
21 function (response) {
22 if (response && !response.error) {
23 //console.log(response.images[0].source);
24
25 // Create facebook post using image
26 FB.api(
27 "/me/feed",
28 "POST",
29 {
30 "message": "",
31 "picture": response.images[0].source,
32 "link": window.location.href,
33 "name": 'Look at the cute panda!',
34 "description": message,
35 "privacy": {
36 value: 'SELF'
37 }
38 },
39 function (response) {
40 if (response && !response.error) {
41 /* handle the result */
42 console.log("Posted story to facebook");
43 console.log(response);
44 }
45 }
46 );
47 }
48 }
49 );
50 },
51 error: function (shr, status, data) {
52 console.log("error " + data + " Status " + shr.status);
53 },
54 complete: function (data) {
55 //console.log('Post to facebook Complete');
56 }
57 });
58}
Resulting Post to Facebook
Posting to Twitter
Posting the image to twitter will also require you to create a Twitter App. Twitter still uses OAuth 1.0, so you will either need to use server-side code or a service such as ouath.io. Unless requested I'll skip the authentication process here for TwitterOAuth which I'm using. The functions used below handle the popup that communicates with server-side code Credits to https://github.com/nobuf/jQuery-OAuth-Popup.
1// Twitter oauth handler
2$.oauthpopup = function (options) {
3 if (!options || !options.path) {
4 throw new Error("options.path must not be empty");
5 }
6 options = $.extend({
7 windowName: 'ConnectWithOAuth' // should not include space for IE
8 , windowOptions: 'location=0,status=0,width=800,height=400'
9 , callback: function () {
10 debugger;
11 //window.location.reload();
12 }
13 }, options);
14
15 var oauthWindow = window.open(options.path, options.windowName, options.windowOptions);
16 var oauthInterval = window.setInterval(function () {
17 if (oauthWindow.closed) {
18 window.clearInterval(oauthInterval);
19 options.callback();
20 }
21 }, 1000);
22};
23// END Twitter oauth handler
24
25//bind to element and pop oauth when clicked
26$.fn.oauthpopup = function (options) {
27 $this = $(this);
28 $this.click($.oauthpopup.bind(this, options));
29};
Posting to Twitter is similar to Facebook in the case that you use FormData, but this is sent to your server-side handler or service instead. You can send the image as a Blob but this is not necessary. The variable window.twit contains authentication data sent from the popup window back to the parent webpage/tab.
1$('#shareTW').click(function () {
2 var dataURL = $('#canvas')[0].toDataURL("image/png");
3 $.oauthpopup({
4 path: '/auth/twitter.php',
5 callback: function () {
6 console.log(window.twit);
7 var data = new FormData();
8 // Tweet text
9 data.append('status', "Look at the cute panda! " + window.location.href + " @jerezb31");
10 // Binary image
11 data.append('image', dataURL);
12 // oAuth Data
13 data.append('oauth_token', window.twit.oauth_token);
14 data.append('oauth_token_secret', window.twit.oauth_token_secret);
15 // Post to Twitter as an update with
16
17 return $.ajax({
18 url: '/auth/share-on-twitter.php',
19 type: 'POST',
20 data: data,
21 cache: false,
22 processData: false,
23 contentType: false,
24 success: function (data) {
25 console.log('Posted to Twitter.');
26 console.log(data);
27 }
28 });
29 }
30 });
31});
Here is the snippet of server-side code that handles the FormData to be sent to twitter using TwitterOAuth. The credentials are verified then the base64 image is uploaded to Twitter's API 'upload/image'
endpoint. The object returned is then added to the list of parameters for posting a status.
1// '/auth/share-on-twitter.php'
2
3require_once '../vendor/abraham/twitteroauth/autoload.php';
4use Abraham\TwitterOAuth\TwitterOAuth;
5
6session_start();
7
8define('CONSUMER_KEY', '*************');
9define('CONSUMER_SECRET', '************');
10define('OAUTH_CALLBACK', '*****************');
11
12//echo '<pre>'.print_r($_REQUEST).'</pre>';
13//exit;
14
15$connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, $_REQUEST['oauth_token'], $_REQUEST['oauth_token_secret']);
16$twitterUser = $connection->get("account/verify_credentials");
17
18$media1 = $connection->upload('media/upload', ['media' => $_REQUEST['image']]);
19$parameters = [
20 'status' => $_REQUEST['status'],
21 'media_ids' => implode(',', [$media1->media_id_string]),
22];
23$result = $connection->post('statuses/update', $parameters);
24echo json_encode($result);