preface

This article mainly introduces how to use Canvas to share screenshots. At first, I thought it was not difficult to share pictures through Canvas painting, but in fact, I still encountered many problems during development, such as:

① The background of the picture is transparent

② Share the picture only has the text without the picture

③ Image cross domain problem

The following is an example: Share pictures, share content description, title, qr code are dynamically generated through the request interface

Implementation: i. Body part

The framework I used was React. Painting sharing adopts native canvas and JS. So don’t worry about VUE, applets, native H5 can also adapt.

Show some inline code slices under the Build Canvas.

//ref is how react gets the Canvas element. You can also use id and get the Canvas using the getElementById() method
// The width and height need to be converted to two to four times to improve the clarity, otherwise it will lead to blurred shared screenshots and lack of clarity
 <canvas ref='canvas' width={1200} height={1600} className={styles.canvasImg}/>
Copy the code

// Click the share button to trigger the this.shareComponent(this.geturlimg) method
// Ignore the rest of the code. Trigger the share function directly
              <div className={styles.luckDraw_viewPrizeBtn} onClick={() = >{
                     this.setState({
                         shareModal:true
                        },() = >{
                            this.shareComponent(this.geturlimg)})}}> Share activity </div>Copy the code

Implementation: ii. JS part

ShareComponent function

The // function takes a callback to convert the canvas to a PNG image after the drawing is complete.
// Canvas mobile cannot save by long press, you must upload as img to save.
shareComponent = (callback) = >{
    let suncode = this.state.suncode // wechat small program sun code
    let activityName = this.state.activityName // Activity title
    let backgroundImg = this.state.backgroundImg / / background
    let postShareDesc= this.state.postShareDesc // Share description field
    let img = new Image()
    img.crossOrigin="anonymous"; // Key, handle image cross domain problem!!
    let _t = this
    // Limit activity titles to 10 characters, more than... omit
    if(activityName.length>10){
        activityName=activityName.slice(0.10) +'... '
      }
    // Since canvas text cannot be wrapped automatically, we need to do a word wrap and limit the number of words to prevent it from going beyond the scope of canvas
    let arrDescribe = [] 
    let maxLeng = postShareDesc.length/20 // Share description 20 words per line, maximum 8 lines
    if(maxLeng= >8){
        maxLeng = 8 // Up to 8 lines
    }
    //postShareDesc indicates the share description field
   
    for(let i = 0; i<maxLeng; i++){// Store the share description field in the arrDescribe array into 20-word lines of up to 8 lines
        let str = postShareDesc.slice(i*20,i*20+20) 
        arrDescribe.push(str)
    }
    // After loading the image, display it in canvas. The image must use onload mode, otherwise it will finish painting before loading the image
    //img is the entire shared image
    img.onload = function (){
      let canvas = _t.refs.canvas // Get the canvas element
      let ctx = canvas.getContext('2d')
        // Set the background color, otherwise it will be transparent
        ctx.fillStyle='#fff';
        ctx.fillRect(0.0.1196.1596);
        ctx.drawImage(img, 0.0.1200.600);
		// Share field description
        ctx.font="52px Arial";
        ctx.fillStyle='# 000';
        Index *100 = 700+index*100 = 700+index*100
        arrDescribe.forEach((item,index) = >{
            ctx.fillText(item,80.700+index*100);
        })
        // Share the title
        ctx.font="64px Arial";
        ctx.fillStyle='# 000';
        ctx.fillText(activityName,520.1320);
		// Share tips
        ctx.font="48px Arial";
        ctx.fillStyle='# 999';
        ctx.fillText('Press small code to view details'.520.1420);
		// Share tips
        ctx.font="48px Arial";
        ctx.fillStyle='# 999';
        ctx.fillText('Share from [XXXX]'.520.1500);
		/ / line
        ctx.moveTo(1120.1160);
        ctx.lineTo(80.1180);
        ctx.strokeStyle="#E8E8E8"
        ctx.stroke();
        //img1 is a small program sun code
        let img1 = new Image()
        img1.crossOrigin="anonymous"; // Key, handle the cross-domain problem when converting sun codes to Base64 format images
        img1.onload = function(){
        ctx.drawImage(img1,  80.1200.340.340Callback (canvas)} assigns the suncode to img1 img1.src = suncode/ / frame
      ctx.strokeStyle="#f5f5f5";
      ctx.rect(0.0.1200.1600);
      ctx.stroke();      

    }
	The timeStamp event attribute returns a timeStamp. Indicates the date and time (milliseconds since the epoch) when the event occurred.
	// Use the URL timestamp to prevent browser caching.
	// Adding a random number or timestamp after the URL is often used to prevent the browser (client) from caching the page. Browser caches are based on URLS,
	// If the page allows caching and the same URL is accessed again before the cache expires, the browser will not send the request to the server again, but will fetch the specified resource directly from the cache.
	// When a random number or timestamp is appended to the end of the URL, it guarantees that a new request will actually be generated each time and that the Web server will not attempt to cache the response from the server.
    const a = `${backgroundImg}? timeStamp=` + (new Date());
    img.src = a

  }
// Once the painting is complete, it must be converted to IMG, otherwise the mobile terminal will not be able to save by long pressing
// The callback must wait until the painting is finished. If converted directly using Canvas. toDataURL('image/ PNG '), the shared image will have only written text and no requested image or text. There are asynchrony issues
  getUrlImg=(canvas) = >{   
    let dataImg = new Image()    
    try {
      dataImg.src = canvas.toDataURL('image/png')}catch (e) {
      console.log(e);
    }
    let urlImg = dataImg.src //urlImg is the img path
    this.setState({urlImg},() = >{})}Copy the code

Implementation: three, Canvas replace IMGS


// Finally, the canvas must be hidden and replaced with IMGS, so that the mobile terminal can long press to save
// CSS. CanvasImg adds display: None to hide the canvas
// Convert the canvas to img and set the width and height of img to 25%
// Img needs to be retracted by 25% because I use a quadruple image to improve the sharpness
    <canvas ref='canvas' width={1200} height={1600} className={styles.canvasImg}/>/ / display: none
    //crossOrigin="Anonymous
    <img src={this.state.urlImg} crossOrigin="Anonymous"/>/ / width: 25%. Height: 25%Div className={styles.sharetips}> Long press to save, can share to friends </div>Copy the code

Summary and optimization

The difficulty is:

(1) Converting canvas into base64 format images will cause cross-domain problems

② Asynchronous problem (the picture is not loaded, the painting has been completed)

③ Background transparency and so on

Optimization:

(1) Clarity: Canvas can be drawn into 2-4 times, converted into a picture and then compressed back to 50%-25%

(2) share photo loading speed: small program qr code sun code, such as background page loading stage can request first, click share button can be directly painting, reduce request time long slow lead to generate the painting problem, but also can avoid the qr code, the background is not loaded, painting has been, lead to draw share figure no background, the problem of qr code.

The friend that has need can refer to study next. If there are shortcomings, welcome to criticize and correct.