5

In my Nuxt PWA I have a function that converts my HTML to Canvas using this package. The generated image is in base 64. Now I want to be able to share that image via: Whatsapp, Facebook, email, Instagram etc. I have found several packages but they all don't seem to support sharing files only URLs and Text.

This is my share function:

shareTicket(index) {
  html2canvas(this.$refs['ticket-' + index][0], {
    backgroundColor: '#efefef',
    useCORS: true, // if the contents of screenshots, there are images, there may be a case of cross-domain, add this parameter, the cross-domain file to solve the problem
  }).then((canvas) => {
    let url = canvas.toDataURL('image/png') // finally produced image url

    if (navigator.share) {
      navigator.share({
        title: 'Title to be shared',
        text: 'Text to be shared',
        url: this.url,
      })
    }
  })

When I take out the if (navigator.share) condition I get an error in my console that navigator.share is not a function. I read somewhere that it only works on HTTPS so I uploaded to my staging server and tried but still got the same error.

Just to be clear I want to be able to share the generated image itself and not a URL.

halfer
  • 19,824
  • 17
  • 99
  • 186
user3718908x100
  • 7,939
  • 15
  • 64
  • 123
  • What happens if you write this all in `if (process.client) { // insert your whole code here }`? And also, it looks like this is mainly used on mobile, did you tried it there or only on desktop? – kissu Jul 13 '21 at 14:11
  • Sorry I dunno what process.client means also this if for both mobile and desktop and I have tried it on both and it doesn't work. – user3718908x100 Jul 13 '21 at 14:18
  • This is from the Nuxt documentation, basically saying that the code should not run on the server. https://nuxtjs.org/docs/2.x/internals-glossary/context Following the documentation of MDN, I achieved to make the demo work properly: https://developer.mozilla.org/en-US/docs/Web/API/Navigator/share You want the same result as on this page, right? – kissu Jul 13 '21 at 14:21
  • When I wrap my code in ```if (process.client)``` I still get the error ```navigator.share``` is not a function. Didn't know about it not working with a server, not sure how to run my app then. Yes please I want to be able share files like described in the link you shared. – user3718908x100 Jul 13 '21 at 14:29
  • Have you set the package up as a [client only plugin](https://nuxtjs.org/docs/2.x/directory-structure/plugins#client-or-server-side-only)? additional help from [this post](https://stackoverflow.com/questions/58694228/using-npm-packages-client-side-with-nuxt) – Porter Jul 21 '21 at 16:21
  • 1
    Sorry what package? I'm confused. – user3718908x100 Jul 21 '21 at 17:21
  • @Porter I'm not using any 3rd party package for this though. – user3718908x100 Jul 24 '21 at 17:24

2 Answers2

8

Tell me if this URL works for you: https://nuxt-share-social-media.netlify.app
If it does, you can find the Github repo here: https://github.com/kissu/so-share-image-bounty

The code is

<template>
  <div>
    <div id="capture" ref="element" style="padding: 10px; background: #f5da55">
      <h4 style="color: #000">Hello world!</h4>
    </div>

    <br />
    <br />
    <button @click="share">share please</button>
  </div>
</template>

<script>
import html2canvas from 'html2canvas'

export default {
  methods: {
    share() {
      // iife here
      ;(async () => {
        if (!('share' in navigator)) {
          return
        }
        // `element` is the HTML element you want to share.
        // `backgroundColor` is the desired background color.
        const canvas = await html2canvas(this.$refs.element)
        canvas.toBlob(async (blob) => {
          // Even if you want to share just one file you need to
          // send them as an array of files.
          const files = [new File([blob], 'image.png', { type: blob.type })]
          const shareData = {
            text: 'Some text',
            title: 'Some title',
            files,
          }
          if (navigator.canShare(shareData)) {
            try {
              await navigator.share(shareData)
            } catch (err) {
              if (err.name !== 'AbortError') {
                console.error(err.name, err.message)
              }
            }
          } else {
            console.warn('Sharing not supported', shareData)
          }
        })
      })()
    },
  },
}
</script>

Inspired from @denvercoder9!


More info about the answer

  • I've used an IIFE because I was not sure how the whole thing works, but it's works great in a method context!
  • I've added a missing async because ESlint and in case you wanted to make something after
  • I've used $refs because this is how you should select specific parts of the DOM in the Vue ecosystem
  • I've striped some things to keep it simple, hosted on Netlify to have some simple HTTPS and tested it on Chrome (v91), working perfectly fine!
  • Here is the link again to the MDN documentation of the Web Share API.

Compatibility

This is my experience for the browsers (tested on the MDN example and on my app, exact same results)

where working
iPad chrome yes
iPad firefox yes
iPad safari yes
windows chrome yes
windows firefox no
android chrome yes
android firefox no
desktop linux chrome no
desktop linux firefox no

For me, this was a mobile only feature (like for Android). But it looks like even some desktop browsers are handling this too. I have to admit that I was really surprised to even see this one work on Windows.
Here is an interesting post from Google that correlates this compatibility: https://web.dev/web-share/#browser-support

Reading MDN again, it says

The navigator.share() method of the Web Share API invokes the native sharing mechanism of the device.

So, I guess that the "(desktop) device" mostly do not support the Share API.

TLDR: this is working totally as intended but the compatibility is really subpar so far.

tony19
  • 125,647
  • 18
  • 229
  • 307
kissu
  • 40,416
  • 14
  • 65
  • 133
  • Thank you soo much for your time. I tried this in both the latest stable builds of firefox, chrome and edge on my Mac running BigSur and it still didn't work. However when I tried it in safari it worked. I tried on my phone as well and it worked with only chrome not firefox. Not sure what's going on here. – user3718908x100 Jul 24 '21 at 14:55
  • So it looks like my code worked :-) Here's a [screenshot](https://user-images.githubusercontent.com/145676/126955107-c9700e92-d436-4490-8fd6-b0ac78e12d94.png) where I test this on desktop Safari. The API is also supported on the desktop by Microsoft Edge (the Chromium-based variant). It will also soon land in macOS and Windows on Chrome. It also already works on Chrome OS. – DenverCoder9 Jul 26 '21 at 08:08
  • @DenverCoder9 yep, it was probably just the funky support + having a proper verified SSL certificate to try this out. – kissu Jul 26 '21 at 08:20
3

I have a variation of the code below in a share() function in an app of mine and it works fine if executed on the client.

const share = async() => {
  if (!('share' in navigator)) {
    return;
  }
  // `element` is the HTML element you want to share.
  // `backgroundColor` is the desired background color.
  const canvas = await html2canvas(element, {
    backgroundColor,
  });
  canvas.toBlob(async (blob) => {
    // Even if you want to share just one file you need to 
    // send them as an array of files.
    const files = [new File([blob], 'image.png', { type: blob.type })];
    const shareData = {
      text: 'Some text',
      title: 'Some title',
      files,
    };
    if (navigator.canShare(shareData)) {
      try {
        await navigator.share(shareData);
      } catch (err) {
        if (err.name !== 'AbortError') {
          console.error(err.name, err.message);      
        }
      }
    } else {
      console.warn('Sharing not supported', shareData);            
    }
  });
};
DenverCoder9
  • 2,024
  • 11
  • 32
  • Does not work. When I take out the condition I get TypeError: navigator.canShare is not a function. Not sure if I require a Vue/Nuxt specific solution for this. – user3718908x100 Jul 20 '21 at 16:55
  • Are you sure you're running this on the client (that is, in the browser, not on the server)? Also, not all browsers support the Web Share Level 2 API, so you need to feature-detect this (`if ('share' in navigator && 'canShare' in navigator) { /* */ }`). Do you have a URL to test this? – DenverCoder9 Jul 22 '21 at 09:59
  • @DenverCoder9 it's a Nuxt app, not sure how i'm supposed to run it. Initially I was using ```yarn dev``` to run my app but I even tried generating the static assets with ```yarn generate``` and then running ```yarn start``` but that didn't work either. – user3718908x100 Jul 24 '21 at 14:47
  • 1
    @user3718908 `yarn dev` is for local dev. If you do have `target: static`, you should `yarn generate` and `yarn start` indeed. You can even `yarn generate` and drop your `dist` directory here directly: https://app.netlify.com/drop – kissu Jul 24 '21 at 21:42