1. ホーム
  2. Web制作
  3. html5

フロントエンドのHtml5でスクリーンショットを共有する方法のサンプルコード

2022-01-21 06:18:18

プリアンブル

この記事では、canvasを使用してスクリーンショットを共有する方法について説明します。
当初、canvasペイントで画像を共有するのは難しくないと思っていたのですが、実際に開発中に多くの落とし穴に遭遇しました
例えば、こんな感じです。
画像の背景が透明
画像なし、文字だけの画像を共有する。
画像クロスドメイン問題
以下の例をご覧ください。
共有画像、共有DeepLコンテンツ、タイトル、およびQRコードはすべて、要求インターフェースを通じて動的に生成されます。

実装する。I. 本体部分

フレームワークはreactを使用し、絵画共有はネイティブのcanvasとjsで実装しています。vueやapplet、ネイティブのH5でも対応可能ですのでご安心ください。

I. キャンバスの構築

以下は、インラインのコード・スニペットです。

//ref is the method that react uses to get the canvas element. You can also use id and get the canvas through getElementById() method.
//The width and height need to be converted to two or four times the image to improve clarity, otherwise it will lead to blurry screenshots and lack of clarity for sharing
 <canvas ref='canvas' width={1200} height={1600} className={styles.canvasImg}/>

//Clicking the share button triggers the this.shareComponent(this.getUrlImg) method
//Non-react framework, 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)
                        })}
                 }>shareActivity</div>

インプリメンテーションを行います。II. JSセクション

shareComponent関数

//The function accepts a callback function for converting the canvas to png image format after the drawing is done.
//canvas mobile can't long press to save, must pass as img to save.
shareComponent = (callback)=>{
    let suncode = this.state.suncode //WeChat small program suncode
    let activityName = this.state.activityName //activity title
    let backgroundImg = this.state.backgroundImg // background image
    let postShareDesc = this.state.postShareDesc // share description field
    let img = new Image()
    img.crossOrigin="anonymous"; //key, handle image cross-domain issues!
    let _t = this
    //limit the active title, up to 10 words, more than ... omit
    if(activityName.length>10){
        activityName=activityName.slice(0,10)+'...'
      }
    // Since canvas text does not automatically wrap, we need to do a text wrap here, as well as a word limit to prevent exceeding the canvas range
    let arrDescribe = [] 
    let maxLeng = postShareDesc.length/20 // share description 20 words per line, max 8 lines
    if(maxLeng=>8){
        maxLeng = 8 //maximum 8 lines
    }
    //postShareDesc is the sharing description field
   
    for(let i = 0;i<maxLeng;i++){
    //divide the share description field into several 20-character rows into the arrDescribe array, and up to 8 rows
        let str = postShareDesc.slice(i*20,i*20+20) 
        arrDescribe.push(str)
    }
    //after the image is loaded, it will be displayed in the canvas, the image must use the onload method, otherwise it will cause the image to finish painting before it finishes loading
    //img is the whole share image
    img.onload = function (){
      let canvas = _t.refs.canvas //get canvas element
      let ctx = canvas.getContext('2d')
        //set the background color, otherwise the background color 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';
        //manual line break, 80 for X coordinate, 700+index*100 for dynamic calculation of Y coordinate
        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);
  //sharing tips
        ctx.font="48px Arial";
        ctx.fillStyle='#999';
        ctx.fillText('Long press applet code to view details',520,1420);
  //sharing tips
        ctx.font="48px Arial";
        ctx.fillStyle='#999';
        ctx.fillText('Shared from [XXXX]',520,1500);
  //Split Line
        ctx.moveTo(1120,1160);
        ctx.lineTo(80,1180);
        ctx.strokeStyle="#E8E8E8"
        ctx.stroke();
        //img1 for the applet sun code
        let img1 = new Image()
        img1.crossOrigin="anonymous"; //key, handle the cross-domain problem when converting suncode to base64 format images
        img1.onload = function(){
        ctx.drawImage(img1, 80, 1200,340,340)
        callback(canvas)

      }
   The suncode is assigned to img1
      img1.src = suncode
   //border
      ctx.strokeStyle="#f5f5f5";
      ctx.rect(0,0,1200,1600);
      ctx.stroke();      

    }
 The //timeStamp event property returns a timestamp. Indicates the date and time (in milliseconds from epoch) when the event occurred.
 //URL timestamp usage: role: To prevent browser caching.
 //URL followed by a random number or timestamp is typically used to prevent browsers (clients) from caching pages. Browser caching is based on the URL for caching.
 //If the page allows caching, then if the same URL is accessed again before the cache timeout, the browser will not send another request to the server side, but will fetch the specified resource directly from the cache.
 // And when a random number or timestamp is appended to the end of the URL, it ensures that a new request is actually generated each time and the web server does not try to cache the response from the server.
    const a = `${backgroundImg}?timeStamp=` + (new Date());
    img.src = a

  }
// must be converted to img after the drawing is done, otherwise the mobile will not be able to long press to save
//you must wait for the painting to finish before you can call back. If you use canvas.toDataURL('image/png') directly to convert, it will result in a shared image with only written dead text and no requested image or text. There will be asynchronous problems
  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},()=>{ 
    })
  }

実装:iii. キャンバスがimgsを置き換える

//finally the canvas must be hidden and then replaced with imgs so that the mobile can long press to save
//add display:none to .canvasImg in css to hide the canvas
//then use the canvas transformed img, and set the img width and height to 25%
//because in order to improve the clarity, I am using quadruple image and then compress it to improve the clarity, so img needs to shrink back to 25%
    <canvas ref='canvas' width={1200} height={1600} className={styles.canvasImg}/>//display:none
    //crossOrigin="Anonymous" Handles image cross-domain issues
    <img src={this.state.urlImg} crossOrigin="Anonymous"/>//width: 25%. height: 25%
    div className={styles.shareTips}>Long press to save and share to friends</div>


まとめと最適化

難しいのは
1.canvasをbase64形式の画像に変換すると、画像のクロスドメイン問題が発生することがある
非同期問題(画像が読み込まれる前に描画が行われる) ②非同期問題(画像が読み込まれる前に描画が行われる
③背景が透けて見える問題など。

最適化です。
①わかりやすさ:キャンバスを2~4倍で描き、画像に変換して50~25%に圧縮して戻すことができる
共有マップの読み込み速度:小さなプログラムQRコード日コード、背景画像や他のページの読み込み段階が最初に要求することができ、共有ボタンをクリックして直接描かれることができ、遅い問題を塗装生成するために長い要求時間を短縮するだけでなく、QRコードを避けることができ、背景画像は、背景画像、QRコードの問題なしで共有マップのうち塗装で生じる、塗装が始まる前にロードされていない。

以上、スクリーンショット共有のサンプルコードをフロントエンドのHtml5で実装する方法について解説しました。Html5の共有スクリーンショットに関連するコンテンツは、Script Houseの過去の記事を検索するか、以下の関連記事を引き続きご覧ください。