1. The background

I have worked on a marketing mobile terminal H5 project – Poster Generator, uploading users’ local picture synthesis posters and supporting download. I have time to sort them out this time.

2. A few highlights

  • Upload local images and support preview

  • Handle ios photo flipping

  • Use canvas to scale and center the image

  • Use canvas to draw images and text

  • Output base64 and support download

3. Upload pictures

Without further ado, file uploads can be supported using the HTML tag. For front-end upload validation, set Accept =”image/*” to limit the file type to image.


<div className="btnUpload" style={{'display':this.state.hasFile && this.state.isUploaded? 'none':'block'}} >
   <input className="upload" type="file" accept="image/ * " ref="imgInput" onChange={this.uploadImg}/>
   <svg className="cameraIcon">
      <use xlinkHref="#camera"></use>
   </svg >
</div>
Copy the code

Gets the currently uploaded image.

var file = event.target.files[0];
Copy the code

To complete the local preview of the image, read the file Data to be processed using the FileReader object and read the file as a DataURL to the page using readAsDataURL. At the same time, in order to deal with the problem of photo turning Angle in ios, it is necessary to first correct the flipped photo. Using the exif-js library, you can get the information about the photo, get the flip Angle, and then use the Canvas canvas to reverse the image.

UploadImg => {var that = this; var file = event.target.files[0]; if (! file) { return; } var orientation = ""; https://github.com/exif-js/exif-js exif. getData(file, function () { orientation = EXIF.getTag(this, 'Orientation'); }); var reader = new FileReader(); ReadAsDataURL (file); readAsDataURL(file); reader.onload = function () { var sourceImg = new Image(); sourceImg.onload = function () { var imgRadio=sourceImg.width/sourceImg.height; var imgStyle={ 'width':imgRadio>1? '100%':'auto', 'height':imgRadio<1? '100%':'auto' } that.setState({isUploaded: true, sourceImg: sourceImg, hasFile: true,previewImgStyle:imgStyle}) }; Async function (context) {sourceimg.src = orientation && orientation! = "1"? await that.rotateImage(context.result, orientation) : context.result; })(this); }; Return new Promise((resolve, reject) => {var image = new image (); var that = this; image.onload = function () { var degree = 0, drawWidth=this.naturalWidth, drawHeight=this.naturalHeight, width, height; var canvas = document.createElement('canvas'); canvas.width = width = drawWidth; canvas.height = height = drawHeight; var context = canvas.getContext('2d'); Case 3: degree = 180 case 3: Degree = 180 case 3: Degree = 180 case 3: Degree = 180; drawWidth = -width; drawHeight = -height; break; Case 6: canvas.width = height; canvas.height = width; degree = 90; drawWidth = width; drawHeight = -height; break; Case 8: Canvas. width = height; canvas.height = width; degree = 270; drawWidth = -width; drawHeight = height; break; Rotate (degree * math.pi / 180); context.drawImage(this, 0, 0, drawWidth, drawHeight); Resolve (canvas. ToDataURL ("image/jpeg", 1)); } image.src = img; }); }Copy the code

html

<div className="preview-box" style={{'display':this.state.hasFile && this.state.isUploaded? 'block':'none'}} onClick={this.changeImg}> <img src={this.state.sourceImg.src} style={this.state.previewImgStyle} className="db-img"/> </div>Copy the code
  • Effect:

4. Crop the image

To the finished picture upload and preview, the synthesis processing posters, background is the size of 640 * 1136, but the uploaded images can be varied, is likely to be square, it is possible that very long -, need to make sure that the picture is not deformation and displays a center part, so you need to compare the width to height ratio of width to height ratio and the background picture, Stretch and compress to the same width or height as the background image, then crop the middle section.

var canvas = document.createElement('canvas'); var ctx = canvas.getContext('2d'); canvas.width = 640; canvas.height = 1136; Var imgRatio = canvas.width/canvas.height; / / the target image. The aspect ratio of var userimgRatio = that state. SourceImg. Width/that state. SourceImg. Height; Var r = (userimgRatio > imgRatio)? (canvas.height / that.state.sourceImg.height) : (canvas.width / that.state.sourceImg.width); var drawObj = { sx: userimgRatio > imgRatio ? (that.state.sourceImg.width - canvas.width / r) / 2 : 0, sy: userimgRatio > imgRatio ? 0 : (that.state.sourceImg.height - canvas.height / r) / 2, sWidth: canvas.width / r, sHeight: canvas.height / r, dx: 0, dy: 0, dWidth: canvas.width, dHeight: canvas.height }; DrawImage (th.state. sourceImg, drawobj. sx, drawobj. sWidth, drawobj. sHeight, drawobj. dx, drawObj.dy, drawObj.dWidth, drawObj.dHeight);Copy the code

Canvas’s drawImage method has a total of nine parameters

As shown in the figure, upload a small square image, calculate the aspect ratio according to the background image, cut out a maximum range in the user image with the same aspect ratio as the background image, and cut out the center part of the image to get the starting point. The upper left corner of this point x coordinate for (that. State. SourceImg. Width – canvas. Width/r) / 2, y is 0, corresponding sx and sy parameters, by wide high and middle part of the picture, the shear wide high (surrounded by four yellow point), That is, swidth in the parameter is canvasi. width/r, sheight is canvasi. height/r, and the final position of the picture is covered with canvas, that is, x=0,y=0, and the final size of the picture is the size of canvas. Finally, “Get the center of the user’s image and fill it with background”

5. Draw pictures

Finally, the background image is drawn together with the uploaded image, and the base64 format is generated, which can be saved by long press.




var newimg = new Image();

        newimg.src = "/src/images/bg.png";

        newimg.onload = function () {

            ctx.drawImage(newimg, 0, 0, canvas.width, canvas.height);

            ctx.fillStyle = "#fff";

            ctx.font = 36 + "px sans-serif";

            ctx.fillText(that.state.slogan1, 50, 1000);

            ctx.fillText(that.state.slogan2, 50, 1070);

            that.setState({

                resultImgUrl: canvas.toDataURL("image/jpeg", .5),

                isGenerated: true

            })

        };

Copy the code

The source address