PK Creative Spring Festival, I am participating in the “Spring Festival Creative Submission Contest”, please see: Spring Festival Creative Submission Contest”
The design Idea
Hi! Hello, we are SoupStudio. Coming to the third year of the epidemic, as an interactive design studio, we came up with the idea of: many people may be stuck in place because of the epidemic, unable to meet their families and friends. Is there any way for people far away to take a New Year photo together?
Thus was born the online campaign “Year of the Tiger Group Photo with the Dragon”. In the H5, users can upload their own photos (which can be anything), and the page will be edited and formatted to create a retro New Year’s poster, which can be shared for others to join.
In order to create a festive atmosphere, we designed three poster themes: “Fireworks”, “slot machines” and “poker players”, each template has its own atmosphere effect. An Easter egg animation can also be triggered when the user clicks on the moving part in the lower right.
Test portrait images come from the Internet
Wechat scanning experience
Technology Sharing
PNG frame by frame animation
In this project we used CSS frame-by-frame animation to implement some animations, such as the ones below
Background fireworks | Trigger full screen fireworks | Tiger head cycle animation |
---|---|---|
The reasons for using PNG frame-by-frame animation are:
- Color is not lost, transparency is supported: GIFs lose color, and only have full transparency and full opacity, so that when used as a overlay, the image must have white jagged edges
- Flexible control of playback: For the previous item, webP format can also be satisfied, but the playback control is not flexible, PNG frame by frame animation can even dynamically adjust the speed in the code, so that in practical design adjustment support is better.
Lottiefiles is also an option, but I’ll leave it at that
Implementation method (take small fireworks as an example) :
- The designer exported the fireworks video as a PNG sequence (that is, each frame of the video was exported as a separate image) and spliced it into Sprite images
The above image is a schematic. The actual layout is vertical and the background is transparent
- Encapsulates a playback component
KeyFrame.vue
, so that it can meet such requirements:
<KeyFrames
w="18vw"
h="18vw"
path="img/sprites.png"
:step="33"
duration="2s"
:iteration-duration="[2000200]"
times="10"
:delay="2000"
:stop="state"
/>
// w, h width height
// path Image path
// How many frames does step have
// duration Specifies the duration of a play
// iterationDuration Duration of multiple plays and pauses
// times Indicates the number of playback times, corresponding to the CSS value
// delay Indicates the delay duration
// stop whether to play or stop
Copy the code
Component design
// All we need is a div, and our Sprite image appears as background-image
// The principle is to use CSS animation property to animate frame by frame. After writing "Steps (1)", we can read background-position of each frame in @keyFrame to animate frame
/ / the vue components
<template>
<div
ref="box"
:class="[$style.box]"
:style="{ width:w, height:h, backgroundImage: `url(${path})`, backgroundPosition: `0 ${stopFrame}%`, animation: stopFrame? null : open? `${cssId} steps(1) ${duration} ${times}` : '', }"
>
</div>
</template>
// Notice that the @keyFrame ID in this animation is dynamically generated. The code is the variable "cssId"
// script<script> // Generate some random ids import newKey from '@/methods/newKey' export default {props: ['w', 'h', 'path', 'step', 'duration', 'times', 'delay', 'iteration-duration', 'stop'], data () { return { cssId: newKey(), frames: [], open: true } }, computed: { stopFrame () { let res = false if (this.stop) { let stepLength = 100 / (this.step - 1) res = this.stop * stepLength } return res } }, //... Create () {// Key code // In general @keyFrame is written in CSS code, Since this component accepts different frames of animation, it needs to dynamically generate @KeyFrame // by assembling the @KeyFrame text from the parameters passed in, Then generate a new <style> tag and insert it in <head> let stepLength = 100 / (this.step-1) for (let I = 0; i < this.step; i++) { this.frames.push({ step: i * stepLength }) } let cssKeyframes = '' for (let i = 0; i < this.frames.length; i++) { let frame = this.frames[i] cssKeyframes = cssKeyframes + `${frame.step}% { background-position: 0 ${frame.step}%; } '} cssKeyframes = '@keyframes ${this.cssId} {${cssKeyframes}} Document.createelement ('style') // Set the style attribute style.type = 'text/ CSS '// write the keyframes style inside the style.innerhtml = CssKeyframes / / store style style to document the head tag. The getElementsByTagName (' head ') [0]. The appendChild (style)}} < / script > / / CSS <style module> .box { background-size: 100%; } </style>Copy the code
Figure keying, stylization and automatic typesetting
In order to allow different people to put it together and integrate it into the poster style, the photos uploaded each time are processed as follows:
A beautiful picture > > > | Using the Matting API > > > | Cut out blank pixels > > > | Add a stroke |
---|---|---|---|
Once you have the image, you can rearrange it, using flex layout to stack the list of images
In the code, the image list is processed into a new array of two images in a layer, layer upon layer, with each layer spaced in a compact and loose style, to create a group photo effect of the design.
Of course, serious people are embarrassed to post their own photos, everyone is playing pranks and Posting their own pets and memes during the game, which is also a kind of unexpected fun 🙂
Here the code implementation is not complex, the following brief description:
- Github.com/liajoy/imag… For stroke
- Github.com/think2011/l… Used to compress pictures, note that it can not handle the problem of rotation of mobile phone photos
- Github.com/exif-js/exi… To handle image rotation, use it to get the image rotation Angle, and then use CSS to adjust the rotation
- Packaging method for Cutting blank screen (not original)
ImgData can be an image address or a base64 URL
function (imgData, callback) {
let img = new Image() // Create a picture object
img.crossOrigin = 'Anonymous'
img.src = imgData
img.onload = function () {
let c = document.createElement('canvas') // Create a processing canvas object
let ctx = c.getContext('2d')
c.width = img.width
c.height = img.height
ctx.drawImage(img, 0.0) / / to draw
let imgData = ctx.getImageData(0.0, c.width, c.height).data // Read the image data
let lOffset = c.width
let rOffset = 0
let tOffset = c.height
let bOffset = 0
for (let i = 0; i < c.width; i++) {
for (let j = 0; j < c.height; j++) {
let pos = (i + c.width * j) * 4
if (imgData[pos] === 255 || imgData[pos + 1= = =255 || imgData[pos + 2= = =255 || imgData[pos + 3= = =255) {
bOffset = Math.max(j, bOffset) // Find the bottom of the color
rOffset = Math.max(i, rOffset) // Find the right end with color
tOffset = Math.min(j, tOffset) // Find the top of the color
lOffset = Math.min(i, lOffset) // Find the left end of the color
}
}
}
lOffset++
rOffset++
tOffset++
bOffset++
let x = document.createElement('canvas') // Create a post-processing canvas object
x.width = rOffset - lOffset
x.height = bOffset - tOffset
let xx = x.getContext('2d')
xx.drawImage(img, lOffset, tOffset, x.width, x.height, 0.0, x.width, x.height) / / to draw
// console.log(x.todataurl ()) // get the final cropped base64
callback(x.toDataURL())
return x.toDataURL()
}
}
Copy the code
The last
This project there are two main challenges: one is to achieve a more exquisite atmosphere animation, another is to dig into the poster style like fusion, we solve these problems, make it an open social activities, to bring some friends during the Spring Festival, also want to see this article you will be able to have fun. I wish you all the best of luck in the New Year.