(By: Kath & kimmy)

Recently, I have been working on a small mobile demo of VUE for several months, among which there is a piece to realize the unified skin changing function of each page. Think of writing an article to write about some of the problems encountered in the implementation process.

Project online Demo

Project online demo (switch to mobile debugging mode)

Github address of the project

Github address of the project

In the demo, there is a subtle way to change the avatar





Modify the picture


Normal upload profile picture with the selection of cutting function, here to achieve a complete picture of the zoom and center display, the next iteration of development to add a custom selection box, mainly introduces the following two implementation

1. Using Transition to make a seamless transition

2. Use directive (vue) to zoom and center images

I’m using Transition to make a seamless transition

Kath: Why do I look good on it? Young people like special effects. Transition has been used mostly in projects for dynamic interactive effects, and it addresses some of vUE’s weaknesses in animation — we can still control the effects we want in data-driven formats, using V-show and V-IF, without too much DOM manipulation. The home page and the info page for changing the profile picture are actually two different pages. To create a seamless transition, I keep the same element of the profile picture on both pages, creating the illusion that the two pages are related






Other people’s appearance effect

  1. Click on the home page to redirect to the INFO page, which triggers the transition from the starting position of the home page to the actual position of the current page. The operation that triggers the info page entry is controlled by defining a Appear Boolean variable for V-show. The effect of rising text also triggers transition when entering, and the interactive effect of entering animation refers to the design style of Ant Design. What is the effect of entering list elements
<! <transition name="slide"> <div class="head-field" V-show ="appear"> <span class="head-field-pic"> <span class="img-hover" @click.stop="uploadHeadImg"> ! [](userinfo.headurl) </span> </span> </div> </transition> ··· data () {return {appear: }}, mounted () {this.$nextTick(() => {this.appear = true})}, ··· <style lang=" SCSS "rel="stylesheet/ SCSS ">. Slide-enter -active,. Slide-leave-active {transform: translateY(0); transition: transform 1s; }.slide-enter,.slide-leave-to/*.fade-leave-active in below version 2.1.8 */ {transform: translateY(-50px); } </style>Copy the code

With regard to the implementation of text effects, here again can popularize the niche usage of small SCSS, my implementation looks like this

<div class="info-field"> <transition name="slide-1"> <p v-show="appear">K.K</p> </transition> <transition name="slide-2"> <p v-show="appear">wanna to be a Brilliant gentle</p> </transition> <transition name="slide-3"> <p V-show ="appear">And a pretty girl</p> </transition> </div> ·· <style lang=" SCSS "rel="stylesheet/ SCSS "> @for $I from 1 to 4 { .slide-#{$i}-enter-active { transform: translateY(0); opacity: 1; transition: transform 1s, opacity 1s; transition-delay: ($i - 1s) / 5; } .slide-#{$i}-leave-active { transform: translateY(0); opacity: 1; transition: transform .5s, opacity .5s; } .slide-#{$i}-enter, .slide-#{$i}-leave-to { opacity: 0; transform: translateY(50px); } } </style>Copy the code

Transition -delay: ($i-1s) / 5; This sentence looks very elegant, the main function is to give them a time difference when entering the field, by adding some modifications to the variables can be created to fit the elegant sequence, in the CSS expression or a sense of accomplishment

2. Leave

The effect of exit is the same as that of entrance, style interaction and definition above, the main thing we need to consider is that the transition takes a little time to complete the series of entrance animations (otherwise the next entry page will immediately appear and the animation will be stopped or overwritten).

beforeRouteLeave (to, from, next) {
    this.appear = false
    setTimeout(() => {
      next()
    }, 800)
  },
Copy the code

Second, use directive to zoom in and center the picture

Like jquery, vue has many plug-ins, and directive is an important part of the development of vue plug-ins. Unlike writing components, most of our components focus on a function or a business block to achieve complete functionality. Then plug-ins I understand as more embedded, for more is the global general, auxiliary nature of the function. For example, bind a V-preview directive to an image to achieve image preview, bind a div directive to achieve popover function, etc. Look at the source code of element. UI and find a lot of things to learn from. For example, I added clickOutside to the project directive, and bound v-MyClickOutside to the corresponding element. A common scenario is that we write the drop-down box ourselves, when the pop-up box, click outside the page will automatically collapse the drop-down box, (instead of the previous we had to listen to the body click event, maybe unbind, write a binding element once kind of), the specific implementation can refer to the project code






Click on the outside of the component to automatically fold


Here I said to bind v-autofix to the picture, realize the function of automatic compression and center display of the picture, to briefly describe the development process of the command plug-in

/* / export default {install (Vue) {let handleImg = (el, binding, vnode) => {if (! el || ! el.parentNode) { return } // console.log('carry', el, binding, El. ParentNode) let img = new Image () let boxWidth = el. ParentNode, offsetWidth img. The onload () = = > {/ / to the length of the small side as a benchmark, scaling, Width < img. Height) {el.style.height = math.floor (img.height/img. Width * boxWidth) + 'px' el.style.width = boxWidth + 'px' el.style.marginTop = -(el.offsetHeight - boxWidth) / 2 + 'px' } else { el.style.width =  Math.floor(img.width / img.height * boxWidth) + 'px' el.style.height = boxWidth + 'px' el.style.marginLeft = -(el.offsetWidth - boxWidth) / 2 + 'px' } } img.src = el.src } Vue.directive('autofix', { inserted (el, binding, vnode) { handleImg(el, binding, vnode) }, update (el, binding, vnode) { handleImg(el, binding, vnode) }, unbind (el) { } }) } }Copy the code

1. directive

First step, as for vue directive, we can register a directive with directive, referring to vue Directive

// Register a global custom directive v-focus vue. directive('focus', {// when the binding element is inserted into the DOM. Inserted: function (el) {// Focus element el.focus()}})Copy the code

There are several hook functions that we can bind to. Their parameters are el, binding, vnode, oldVnode, etc. Called the first time an element is bound, which defines an initialization action to be performed once inserted, unlike the INSERTED, which happens when the node is generated, but the DOM has not yet been inserted, so you’ll find that your attempt to get el.parentNode from the hook fails. Inserted: Called when the bound element is inserted into the parent. If we want our action to be executed once, but need to be associated with other nodes (get the parent element’s width and height, modify their attribute values, etc.), then we should execute our action on the INSERTED node. Update: This hook executes any node change, property change, etc., so it can be used as a listener event, and it has parameters such as oldValue that other hooks do not have, so we can determine if the change requires our operation. Unbind: Called only once, when an instruction is unbound from an element.

2. Know what we need

What we need is something that binds a V-autofix directive to the image. When the image SRC changes (we get the uploaded image, modify the image SRC), it will automatically compress the image to the size of our div according to their width and height.





The actual figure





rendering

So we can determine when we want to fire, one when the page loads, and one when SRC changes, so we can determine to use bind/inserted and update as hook functions

  1. Bind/INSERTED and Update both provide us with el, binding object equivalents, and we think that the way we get the image width and height is to wait for the image to load, and then get the img width and height, so, We can do this by getting the element SRC, loading the image with a new image, and getting the corresponding width and height
      let img = new Image()
      img.onload = () => {
      // get img.width
      // get img.height
      }
      img.src = el.src
Copy the code

Next, we can calculate the aspect ratio and scale the image with the smallest width or height

Let img = new Image() img.onload = () => { Width < img. Height) {el.style.height = math.floor (img.height/img. Width * boxWidth) + 'px' el.style.width = boxWidth + 'px' } else { el.style.width = Math.floor(img.width / img.height * boxWidth) + 'px' el.style.height = boxWidth + 'px' } } img.src = el.srcCopy the code

The final step is to center the image. Here I center the image by defining the parent element at the top of the image, with the img offset half the width

let handleImg = (el, binding, vnode) => { if (! el || ! el.parentNode) { return } // console.log('carry', el, binding, El. ParentNode) let img = new Image () let boxWidth = el. ParentNode, offsetWidth img. The onload () = = > {/ / to the length of the small side as a benchmark, scaling, Width < img. Height) {el.style.height = math.floor (img.height/img. Width * boxWidth) + 'px' el.style.width = boxWidth + 'px' el.style.marginTop = -(el.offsetHeight - boxWidth) / 2 + 'px' } else { el.style.width =  Math.floor(img.width / img.height * boxWidth) + 'px' el.style.height = boxWidth + 'px' el.style.marginLeft = -(el.offsetWidth - boxWidth) / 2 + 'px' } } img.src = el.src }Copy the code

Note that I need to get the parent element width, so BIND cannot get the parent element, so I have to use the INSERTED element. This is a relatively simple instruction application, and currently only uses the EL operation, but there are more complete implementations, which we need to explore

Finally, the function of modifying avatar is almost nothing good to talk about here, as long as the display, the rest of the work, I just put the updated picture into Base64 and save in localStorage, a lot of advice.