Spread the picture on social media using Progressive Web App

I am currently working on a Nuxt PWA where I have implemented a function to convert HTML to Canvas using a specific package. The output generated is in base 64 format. My goal now is to find a way to easily share this image through various platforms such as Whatsapp, Facebook, email, and Instagram. Despite my efforts, most packages I found only support sharing URLs and text, not files.

Below is the code for my sharing function:

shareTicket(index) {
  html2canvas(this.$refs['ticket-' + index][0], {
    backgroundColor: '#efefef',
    useCORS: true,
  }).then((canvas) => {
    let url = canvas.toDataURL('image/png')

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

Removing the if (navigator.share) condition results in an error in the console stating that navigator.share is not a function. It has been mentioned that it only works with HTTPS, so I even tried uploading to a staging server, but the error persisted.

To clarify, my main objective is to directly share the actual generated image rather than just a URL link.

Answer №1

Could you please check if this URL is accessible to you:
If it is, you can access the Github repository here: https://github.com/kissu/so-share-image-bounty

The code snippet provided 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 implementation
      ;(async () => {
        if (!('share' in navigator)) {
          return
        }
        const canvas = await html2canvas(this.$refs.element)
        canvas.toBlob(async (blob) => {
          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>

Credit goes to @denvercoder9 for inspiration!


Additional Notes

  • I've utilized an Immediately Invoked Function Expression (IIFE) to work within the method context.
  • An asynchronous function was added for completeness and adherence to guidelines of ESlint.
  • I've employed $refs to accurately select DOM elements within Vue.
  • The solution was streamlined for simplicity, hosted securely on Netlify with HTTPS, and thoroughly tested on Chrome (v91).
  • For reference, here's the MDN documentation link for the Web Share API compatibility.

Web Browser Compatibility

My findings regarding browser compatibility based on testing are summarized in the table below:

Browser Support Status
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

While initially perceived as a mobile-specific feature, some desktop browsers surprisingly exhibit support for the Web Share API. It's noteworthy that Windows showed decent functionality in my tests. For further insights on browser support, refer to Google's post on browser compatibility.

In conclusion, based on the official definition from MDN:

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

Hence, the Share API seems more suited for mobile devices than desktop systems.

TLDR: The functionality performs as expected, but its compatibility across browsers remains relatively limited.

Answer №2

In my app, I have a customized version of the code snippet below placed within a share() function, which runs smoothly when executed on the client-side.

const share = async() => {
  if (!('share' in navigator)) {
    return;
  }
  // `element` represents the HTML element to be shared.
  // `backgroundColor` specifies the background color.
  const canvas = await html2canvas(element, {
    backgroundColor,
  });
  canvas.toBlob(async (blob) => {
    // Even for sharing a single file, it needs to be sent 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);            
    }
  });
};

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is there a sleek way to create a JavaScript function that can provide two string values as output?

I am looking to store values in a specific format: "somekey": "value1", "value2" My goal is to create a function that takes in "somekey" and returns "value1", "value2". Alternatively, I could initialize a collection in my JavaScript file and reference i ...

The issue with implementing simple getElementsByClassName JavaScript in the footer of a WordPress site persists

I am facing an issue with a 1-liner JavaScript code in the footer where getElementsByClassName function doesn't seem to work for tweaking style attributes. The text "Hello World" is displaying fine, so I suspect it might be a syntax error? Here' ...

Filtering Array Elements in Vue JS: A Step-by-Step Guide

In my Vue Js 2.0 application, I am working on filtering elements in an array format. Here is the code snippet: const search = this.search_by_name.toLowerCase() const searchContact = this.search_by_contact.toLowerCase() return this.meetings .map(i => ...

Ensuring User Input Integrity with JavaScript Prompt Validation

I need help with validating input from a Javascript prompt() in an external js file using HTML code. I know how to call the Javascript function and write the validation logic, but I'm unsure how to handle the prompt and user input in HTML. Do I need ...

Interact with the horizontal click and drag scroller to navigate through various sections effortlessly, each designed to assist you

Currently, I am facing an issue with my horizontal scrolling feature in JavaScript. It works perfectly for one specific section that has a particular classname, but it fails to replicate the same effects for other sections that share the same classname. ...

When creating an async function, the type of return value must be the universal Promise<T> type

https://i.stack.imgur.com/MhNuX.png Can you explain why TSlint continues to show the error message "The return type of an async function or method must be the global Promise type"? I'm confused about what the issue might be. UPDATE: https://i.stac ...

What is the best way to create JavaScript code specifically for devices with a maximum width of 520px?

Is there a way to apply this JavaScript code specifically to devices with a maximum width of 520px? I could use some guidance on how to achieve this. // Apply code for max-width = 520px const myBtn = document.getElementById("darktheme"); const ...

Obtaining the referring URL after being redirected from one webpage to another

I have multiple pages redirecting to dev.php using a PHP header. I am curious about the source of the redirection. <?php header(Location: dev.php); ?> I attempted to use <?php print "You entered using a link on ".$_SERVER["HTTP_REFERER"]; ?> ...

What is the best method to retrieve the minimum and maximum values of a range slider in PHP and

I've successfully implemented a custom range slider using the code snippet below: HTML: <input type="text" class="salary" id="salary" name="salary" style="border:0; color:#f6931f; font-weight:bold;&qu ...

How to achieve an endless cycle using Promise recursion in a NodeJS environment

I am planning to replace my blocking infinite while loop with promises. My run function is quite simple - it lights up an LED and then turns it off before moving on to the next one. Since Promises do not work inside while loops, I'm exploring how I c ...

issues encountered when trying to integrate bootstrap.js in Django framework

My website is built using the Django templating engine with Bootstrap for design and basic JavaScript. While the CSS components from Bootstrap are working fine, I'm having trouble getting the JavaScript to work: {% load staticfiles %} <!d ...

Extract the response data following a axios post request (using vue and laravel)

My goal was to devise a unique login system utilizing vue for the frontend, laravel for handling the API, and axios as the intermediary between the two. The challenge I faced was retrieving the data returned from my AuthController. Below are my code snippe ...

trouble with maintaining nodejs mariadb connection

Hello, I am working with nodejs to create a rest API However, I have encountered an issue Let's take a look at the code var http = require('http'); var url = require('url'); var mariadb = require('mariadb'); http.c ...

The size of the array within the object does not align

I've run into a roadblock while attempting to implement the tree hierarchy in D3. Initially, I believed that I had correctly structured the JSON data, but upon inspecting the object using Developer's Tool, a discrepancy caught my eye: https://i. ...

What is the reason behind fullstack-angular generator utilizing Lo-Dash's merge rather than document.set?

This is the original code snippet used for updating: exports.update = function(req, res) { if(req.body._id) { delete req.body._id; } Thing.findById(req.params.id, function (err, thing) { if (err) { return handleError(res, err); } if(!thing) { ...

Converting a one-dimensional array into a two-dimensional array in JavaScript explained

Below is the code snippet const arrayColumn = (arr, n) => arr.map(x => x[n]); const pcorr = (x, y) => { let sumX = 0, sumY = 0, sumXY = 0, sumX2 = 0, sumY2 = 0; const minLength = x.length = y.length = Math.min(x.length, y.le ...

Having trouble with opening and closing popup windows in JavaScript while using Android frames?

To display additional information for the user, I create a new tab using the following code: window.open("/Home/Agreement", "_blank"); Within the Agreement View, there is a button with JavaScript that allows the user to close the Popup and return to the m ...

Is there a way to automatically increase a value by clicking on it?

Check out this code snippet I'm working with: let funds = document.createElement('funds') funds.style.color = 'green' funds.style.textAlign = 'center' funds.style.fontSize = '50px' funds.style.backgroundCol ...

VueApp requires 'v-bind:key' directives for elements in iteration to function properly

Help needed with this error message: [vue/require-v-for-key] Elements in an iteration require 'v-bind:key' directives to be included. View the code on Codepen - here If you have a solution for this issue, please share! ...

Issue with applying custom styles in Shopware 6 plugin SCSS components

Currently, I am enhancing a Shopware 6 store and attempting to customize the default styles of the sw-extension-card-base component. To achieve this, I have developed the following files: The content of my index.js is as follows: import template from &apo ...