Realizing the functions of omitting, expanding and folding multi line text — electron + Vue writing player (2)

Time:2020-9-26

order


Today, we continue to ponder over the details of the user experience of the player. We found that if the description words on the detail pages of the main player’s song list, singer, album and MV are too long, they will automatically fold up and provide an expand button. Click to expand. It’s really convenient. Let’s have a look at our own rolling implementation
Realizing the functions of omitting, expanding and folding multi line text -- electron + Vue writing player (2)
It’s too fast to see clearly. It’s a waste of time to roll slowly.

Or to realize the expansion and folding function of the long text

Design


This function is not difficult to implement. First of all, an element is needed to store the description text. The height of this element is the height of the text itself. Many users like to use line breaks and blank lines to format beautiful text. In order to keep the newline and empty line content of the text itself, I use it here<pre>Element as a container, (you can also set thewhite-spaceThen you need a fixed height parent container, and set the overflow hidden. Then, place an expand / collapse button. Click expand to cancel the height setting of the parent container and click collapse to restore the fixed height of the parent container. The main work is completed. It is very simple. Finally, you can beautify the button.

realization


Let’s take a look at the actual effect after completion
Realizing the functions of omitting, expanding and folding multi line text -- electron + Vue writing player (2)

Click here to checkSimple demo

Since the expansion and collapse function needs to be used in many pages, it is considered to extract it into a general-purpose component. First of all, you need to declare what you want to receiveprops

props: {
    description: {
        type: String,
        default: ''
    }, // description text
    lineNumbers: {
        type: Number,
        default: 3
    }, // the maximum number of displayed rows in the collapsed state
    lineHeight: {
        type: Number,
        default: 22
    }, // row height
    fontSize: {
        type: Number,
        default: 14
    }, // font size
    btnOutOfWords: {
        type: Boolean,
        default: true
    }// does the expand button not cover the text
 },

These are simple configurations. Next, you need to use some internal data to control the operation of the components,

data () {
    return {
        Deschasoverflow: false, // whether the content overflows
        Showmore: false, // expanded
        Savedescription: '// the description after escape
    }
}

When the component is initialized and every time the description content changes, we need to judge whether the new content overflows the parent container, and record the result in thedescHasOverflowThe expand button will be displayed only when the overflow occurs. The skill of judging overflow is also very simpleclientHeightandscrollHeightThe judgment logic is encapsulated in theinitMethod

init () {
    this.descOverflow  =False // restore non overflow state
    this.showMore  =False // restore stowed state
    this.saveDescription  = this.saveDescription  & & this.description.replace (/ [<] / g, '& lt;') // escape < symbol
    //What happens if you don't use this. $nexttick?
    this.$nextTick(() => {
        const desc = this.$refs.desc
        if (desc.clientHeight < desc.scrollHeight) {
            this.descOverflow = true
        }
    })
}

Next, in the componentmountedAnd listen todescriptionCall on changeinitMethod is enough

 watch: {
    description () {
        this.init()
    }
 },
 mounted () {
    this.init()
 }
 

If the parent component is loaded through a route, it will not trigger itself and its child components when jumping between the same routesmountedHook, if you put it in<keep-alive>Internal, then it and its subcomponents will only fire from beginning to endmountedHook once, so only inmountedHook internal callinitNot enough.

Define a method to respond to button events

 handleShowMore () {
    this.showMore = !this.showMore
 }
 

Inside the template according toshouMoreDifferent styles can be applied to the parent container element and the expand / stow button

<template>
    <div class="desc-box" :class="{'desc-show-full': showMore}">
        <div
            class="desc-content"
            :style="{
                'height': showMore ? 'auto' : (lineNumbers + 1) * lineHeight + 'px',
                'font-size': fontSize + 'px',
                'line-height': lineHeight + 'px'
            }"
            ref="desc"
        >
            <span>Introduction:</span>
            <pre v-if="description && description.length > 0">{{ saveDescription }}</pre>
            < span v-else > none</span>
        </div>
        <div class="show-more" v-if="descOverflow" :style="{'right': btnOutOfWords ? '-30px' : '6px'}">
            <em></em>
            <a class="more_ link" @ click.prevent= "Handleshowmore" > {showmore? '^ collapse': '... Expand'}}</a>
        </div>
    </div>
</template>

Finally, add a hundred million points of CSS code to beautify it. Click here to seeComplete code

last

As mentioned aboveinitMethod, we’re listeningdescriptionAfter the change, let the code that operates DOM in the$nextTickMethod. If you do not use this method, you will immediately synchronize the calculated elementsclientHeightandscrollHeightIn this case, the DOM element participating in the calculation isdescriptionResults before change (if only inmountedHook, because the virtual DOM in Vue is rendered asynchronously, it merges all updates to the virtual DOM after the synchronization code is executed, such as

example () {
    for (let i = 0; i < 100; i++) {
        this.a = i
    }
}

When callingexampleThe method was changed a hundred timesthia.aThe virtual DOM update is triggered only once.

Our goal is to calculate the height after DOM update, while Vue provides$nextTickIt is for this reason that the method is born

To wait for Vue to finish updating the DOM after the data changes, you can use it immediately after the data changesVue.nextTick(callback)。 In this way, the callback function is called after the DOM update is completed.

It uses it internallyMutationObserverTo monitor the modification of DOM elements. Once the DOM element is modified, the incoming callback will be called to ensure that the DOM has been updated when the callback function is executed.