微信刷屏的「给我一面国旗」如果要做到,技术原理是什么?

  • 2019 年 10 月 7 日
  • 筆記

十年前,在qq群里流传的是这么一段话。

十年后,朋友圈里出现了这么一群人。

时代在变,但是套路没变。割完一茬韭菜,总会有新的一茬韭菜长出来了。

但如果真要做到,内含多少种技术原理呢?场主做了大胆推测:

首先不可能@微信官方,就给你duang一下换好头像。

合理的打开方式是,进入它的活动页面,然后存出加了国旗的头像,最后手动换上。

第一步:先用photoshop做好活动页面,然后切图

第二步:用 HTML5+CSS3+JS 制作出页面

拷贝的活动页代码,侵删,仅供学习参考

<!DOCTYPE html>  <html lang="en">   <head>    <meta charset="utf-8" />    <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />    <meta name="description" content="迎国庆换新颜" />    <meta name="keywords" content="" />    <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no" />    <meta name="renderer" content="webkit" />    <meta name="format-detection" content="telphone=no,email=no,address=no" />    <meta name="apple-mobile-web-app-capable" content="yes" />    <meta http-equiv="x-dns-prefetch-control" content="on" />    <link rel="dns-prefetch" href="//open.mobile.qq.com" />    <link rel="dns-prefetch" href="//pingjs.qq.com" />    <link rel="dns-prefetch" href="//qnlite.gtimg.com" />    <link rel="preconnect" href="//qnlite.gtimg.com" />    <link rel="preconnect" href="//open.mobile.qq.com" />    <link rel="preconnect" href="//pingjs.qq.com" />    <link rel="icon" href="https://qnlite.gtimg.com/qqnewslite/logo.png" />    <title>迎国庆换新颜</title>    <script>!function(){var i=0;!function e(t){var n=40;(document.documentElement.clientWidth!==window.innerWidth||0===document.documentElement.clientWidth&&0===window.innerWidth)&&i<10?(document.documentElement.style.opacity=0,window.setTimeout(e,0),i++):(document.documentElement.style.opacity=0,setTimeout(function(){var e=500<window.innerWidth?500:window.innerWidth;n=parseInt(e/750*1e4*40)/1e4,document.documentElement.style.opacity=1,document.documentElement.style.fontSize=n+"px"},0))}()}()</script>    <script src="https://mat1.gtimg.com/bbs/qqnewslite/js/TGMobileShare-noadtag19.min.js"></script>    <script>!function(n){"use strict";n.loadCSS||(n.loadCSS=function(){});var o=loadCSS.relpreload={};if(o.support=function(){var e;try{e=n.document.createElement("link").relList.supports("preload")}catch(t){e=!1}return function(){return e}}(),o.bindMediaToggle=function(t){function e(){t.media=a}var a=t.media||"all";t.addEventListener?t.addEventListener("load",e):t.attachEvent&&t.attachEvent("onload",e),setTimeout(function(){t.rel="stylesheet",t.media="only x"}),setTimeout(e,3e3)},o.poly=function(){if(!o.support())for(var t=n.document.getElementsByTagName("link"),e=0;e<t.length;e++){var a=t[e];"preload"!==a.rel||"style"!==a.getAttribute("as")||a.getAttribute("data-loadcss")||(a.setAttribute("data-loadcss",!0),o.bindMediaToggle(a))}},!o.support()){o.poly();var t=n.setInterval(o.poly,500);n.addEventListener?n.addEventListener("load",function(){o.poly(),n.clearInterval(t)}):n.attachEvent&&n.attachEvent("onload",function(){o.poly(),n.clearInterval(t)})}"undefined"!=typeof exports?exports.loadCSS=loadCSS:n.loadCSS=loadCSS}("undefined"!=typeof global?global:this)</script>    <link href="https://qnlite.gtimg.com/qqnewslite/css/chunk-nationaldayhead-vendors.0f433423.css" rel="preload" as="style" onload="this.onload=null,this.rel=&quot;stylesheet&quot;" />    <noscript>     <link href="https://qnlite.gtimg.com/qqnewslite/css/chunk-nationaldayhead-vendors.0f433423.css" rel="stylesheet" />    </noscript>   </head>   <body>    <div id="app"></div>    <script>function changeThemeType(){}</script>    <script src="https://qnlite.gtimg.com/qqnewslite/js/chunk-vendors.836075e1.js"></script>    <script src="https://qnlite.gtimg.com/qqnewslite/js/chunk-nationaldayhead-vendors.0ea1c589.js"></script>    <script src="https://qnlite.gtimg.com/qqnewslite/js/nationaldayhead.5241bf47.js"></script>   </body>

第三步:获取微信用户的头像

1、用户同意授权,获取code

2、通过code换取网页授权access_token

3、刷新access_token(如果需要)

4、拉取用户信息(需scope为 snsapi_userinfo)

5、检验授权凭证(access_token)是否有效

具体看这里→_→:

https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html

最后一步:把覆盖了国旗透明素材的头像导出来

用html2canvas.js,具体:

1、生成图片可以用canvas,html2canvas开源库引用

/**   * 根据window.devicePixelRatio获取像素比   */  function DPR() {      if (window.devicePixelRatio && window.devicePixelRatio > 1) {          return window.devicePixelRatio;      }      return 1;  }  /**   *  将传入值转为整数   */  function parseValue(value) {      return parseInt(value, 10);  };  /**   * 绘制canvas   */  async function drawCanvas (selector) {      // 获取想要转换的 DOM 节点      const dom = document.querySelector(selector);      const box = window.getComputedStyle(dom);      // DOM 节点计算后宽高      const width = parseValue(box.width);      const height = parseValue(box.height);      // 获取像素比      const scaleBy = DPR();      // 创建自定义 canvas 元素      var canvas = document.createElement('canvas');      // 设定 canvas 元素属性宽高为 DOM 节点宽高 * 像素比      canvas.width = width * scaleBy;      canvas.height = height * scaleBy;      // 设定 canvas css宽高为 DOM 节点宽高      canvas.style.width = `${width}px`;      canvas.style.height = `${height}px`;        // 获取画笔      const context = canvas.getContext('2d');        // 将所有绘制内容放大像素比倍      context.scale(scaleBy, scaleBy);        let x = width;      let y = height;      return await html2canvas(dom, {canvas}).then(function () {          convertCanvasToImage(canvas, x ,y)      })  }    /**   * 图片转base64格式   */  function convertCanvasToImage(canvas, x, y) {      let image = new Image();      let _container = document.getElementsByClassName('container')[0];      let _body = document.getElementsByTagName('body')[0];      image.width = x;      image.height = y;      image.src = canvas.toDataURL("image/png");      _body.removeChild(_container);      document.body.appendChild(image);      return image;  }  drawCanvas('.container')

2、由于现在的手机都是高清屏,所以如果你不做处理就会出现模糊的情况,设备像素比 devicePixelRatio js 提供了 window.devicePixelRatio 可以获取设备像素比。

function DPR() {          if (window.devicePixelRatio && window.devicePixelRatio > 1) {              return window.devicePixelRatio;          }          return 1;      }

这个DPR函数就是获取设备的像素比, 那获取像素比之后要做什么呢?

var canvas = document.createElement('canvas');          // 设定 canvas 元素属性宽高为 DOM 节点宽高 * 像素比          canvas.width = width * scaleBy;          canvas.height = height * scaleBy;          // 设定 canvas css宽高为 DOM 节点宽高          canvas.style.width = `${width}px`;          canvas.style.height = `${height}px`;            // 获取画笔          const context = canvas.getContext('2d');            // 将所有绘制内容放大像素比倍          context.scale(scaleBy, scaleBy);

3、获取设备像素比之后将canavs.width 和 canvas.height 去乘以设备像素比,也就是 scaleBy; 这个时候在去设置canvas.style.width 和 canvas.style.height 为dom的宽和高。

6plus DPR=3

4、最后调用canvas.toDataURL("image/png");赋值给image.src,由于微信里面无法保存图片,所以只能生成图片文件,调用微信自带的长按保存到图片到相册功能。