This article was originally published at: github.com/bigo-fronte… Welcome to follow and reprint.

preface

Recently, I often received the demand of customized pictures for users on the activity page, so I made a summary of the business functions of picture cutting and some pits stepped on before, hoping to be of reference value to everyone.

Requirements describe

Let’s take a look at the overall requirements functionality:

  • Clicking on a fixed area allows the user to select the photo in the photo/gallery
  • The user can then zoom and drag the selected photo to adjust the photo display area
  • After completing the picture, users to be adjusted can also choose different activity theme covers to synthesize and generate live cover related to the event

Function implementation

  • The first step from the photo, album function, this step can be directly usedinputThe tag invokes native photo and album functions.
<input type="file" accept="image/jpeg, image/png" @change="selectPhoto" />
Copy the code
  • The second step involves image cropping, zooming, dragging and other functions, which we introduced hereAlloyCrop Image clipping libraryIn thebodyThe bottom can be introduced afterVueCall directly from:
<script src="<%= BASE_URL %>alloy-crop.js"></script>
Copy the code
new AlloyCrop({
  image_src: file,
  width: clientWidth,
  height: clientWidth,
  output: 1.className: "m-clip-box".ok(base64) {
    that.picUrl = base64;
    that.prewObj.destroy();
  },
  ok_text: "Ok".cancel_text: "Cancel".cancel(){ that.prewObj.destroy(); }});Copy the code
  • Directly introducingAlloyCropLater, I found that the UI style displayed was inconsistent with that in our design drawing. After checking the document, I found no configuration to customize the style, so I directly used itchromedomCheck the tool, find the corresponding node to obtain the class name and then style overwrite.
  • The next step is to choose different covers for composition, which can be used herecanvasDraw it, and pass itcanvas.toDataURLExport the image we need:
drawCanvas() {
  const canvas = document.getElementById('a3');
  const context = canvas.getContext('2d');
  context.clearRect(0.0.480.480);

  const imageBg = new Image();
  const imageHeadBg = new Image();
  imageBg.setAttribute('crossOrigin'.'anonymous');
  imageHeadBg.setAttribute('crossOrigin'.'anonymous');
  const imgList = [];

  function allImgLoad(resolve) {
    if (imgList.length === 2) {
      // Draw the background
      context.drawImage(imageBg, 0.0.480.480);
      context.drawImage(imageHeadBg, 0.0.480.480);
      resolve(canvas.toDataURL('image/jpeg'));
    }
  }

  imageBg.src = this.picUrl;
  imageHeadBg.src = this.selectCover;

  return new Promise((resolve) = > {
    imageBg.onload = () = > {
      console.log('image1 has loaded');
      imgList.push(1);
      allImgLoad(resolve);
    };
    imageHeadBg.onload = () = > {
      console.log('image2 has loaded');
      imgList.push(1);
      allImgLoad(resolve);
    };
  });
},
Copy the code

Problems encountered

IOS photo rotation 90 degree problem

  • In the process of self-testing, it was found that the picture would rotate 90 degrees on IOS devices after taking photos. After investigation, the reason is that IOS devices have different ways of judging the picture landscape/portrait in the process of taking photos, so we need to pass the picture according to the picture parameterscanvasRotate it in the direction we expect.
  • Exif. js library is a relatively mature library in the field of image attribute acquisition. Its principle is to obtain the parameters of the image when taking photos, including Orientation label, according to the binary data of the image. After importing the library, call as follows:
// Get the direction of the image
function getPhotoOrientation(img) {
  var orient;
  EXIF.getData(img, function () {
    orient = EXIF.getTag(this."Orientation");
    console.log("orient2", orient);
  });
  return orient;
}
Copy the code
  • Given that we’re trying to get the pictureOrientationProperty, introduced a 40K library, after viewing the source code found that it also contains a lot of image processing methods we do not need, so finally found a simplified version of the function, useDataViewandreadAsArrayBufferIs enough to meet our needs:
function getOrientation(file, callback) {
  const reader = new FileReader();
  // eslint-disable-next-line func-names
  reader.onload = function (e) {
    const view = new DataView(e.target.result);
    if (view.getUint16(0.false)! = =0xffd8) {
      return callback(-2);
    }
    const length = view.byteLength;
    let offset = 2;
    while (offset < length) {
      if (view.getUint16(offset + 2.false) < =8) return callback(-1);
      const marker = view.getUint16(offset, false);
      offset += 2;
      if (marker === 0xffe1) {
        // eslint-disable-next-line no-cond-assign
        if (view.getUint32((offset += 2), false)! = =0x45786966) {
          return callback(-1);
        }

        const little = view.getUint16((offset += 6), false) = = =0x4949;
        offset += view.getUint32(offset + 4, little);
        const tags = view.getUint16(offset, little);
        offset += 2;
        for (let i = 0; i < tags; i += 1) {
          if (view.getUint16(offset + i * 12, little) === 0x0112) {
            return callback(view.getUint16(offset + i * 12 + 8, little)); }}// eslint-disable-next-line no-bitwise
      } else if ((marker & 0xff00)! = =0xff00) {
        break;
      } else {
        offset += view.getUint16(offset, false); }}return callback(-1);
  };
  reader.readAsArrayBuffer(file);
}
Copy the code
  • To get toOrientationAttribute meanings are as follows:
  • Use after obtaining the directioncanvasRotate image processing:
resetOrientation(srcBase64, cb) {
  const img = new Image();
  const { Orientation } = this;

  img.onload = () = > {
    const { width, height } = img;
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

    if (Orientation > 4 && Orientation < 9) {
      canvas.width = height;
      canvas.height = width;
    } else {
      canvas.width = width;
      canvas.height = height;
    }

    // transform context before drawing image
    switch (Orientation) {
      case 2:
        ctx.transform(-1.0.0.1, width, 0);
        break;
      case 3:
        ctx.transform(-1.0.0, -1, width, height);
        break;
      case 4:
        ctx.transform(1.0.0, -1.0, height);
        break;
      case 5:
        ctx.transform(0.1.1.0.0.0);
        break;
      case 6:
        ctx.transform(0.1, -1.0, height, 0);
        break;
      case 7:
        ctx.transform(0, -1, -1.0, height, width);
        break;
      case 8:
        ctx.transform(0, -1.1.0.0, width);
        break;
      default:
        break;
    }

    ctx.drawImage(img, 0.0);
    const rs = canvas.toDataURL('image/jpeg');

    cb(rs);
  };

  img.src = srcBase64;
},
Copy the code

Canvas export result is empty problem

  • Snapshots are found in ios12canvas.toDataURLIt keeps coming back"data:,"Causes the image to be empty.
  • Description in MDN is as follows:
  • It turned out to be a full-screen screenshotcanvasThe size exceeds the viewable area of the WebView. Whereas different WebViewsmaximum canvas sizeThe problem was solved by making the screenshot area always smaller than the viewable area width.

conclusion

BigoLive revenue As a global live streaming platform, has a large user base. Different from the booming Internet environment in China, many overseas users’ operating systems and network environments are still at the middle and low-end level. All kinds of terminal models, complex code running environment and network environment pose greater challenges to front-end development. We need to continue to accommodate more and more user groups to fulfill the basic business functions.

Welcome everyone to leave a message to discuss, wish smooth work, happy life!

I’m bigO front. See you next time.