Guide for creating a smooth fade in/out effect for the "Show More" feature in Vue

This is my current Lorem Ipsum page.
When I click the Lorem button, the text will be expand or collapse. https://i.sstatic.net/BldW4.png

I attempted to modify the css below to create a fading effect for the hidden text.

@keyframes open {
  from {
    line-clamp: 3;
    -webkit-line-clamp: 3;
    opacity: 0; //new
  }
  to {
    line-clamp: initial;
    -webkit-line-clamp: initial;
    opacity: 1; //new
  }
}

@keyframes close {
  from {
    line-clamp: initial;
    -webkit-line-clamp: initial;
    opacity: 1; //new
  }
  to {
    line-clamp: 3;
    -webkit-line-clamp: 3;
    opacity: 0; //new
  }
}

After incorporating this code, I discovered that the original text is also hidden.

How can I rectify this issue?

App.vue

<template>
  <div id="app">
    <div :class="{ box, open: showMore }">
      <div class="top">
        <h1>Show More</h1>
      </div>
      <p class="text">
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
        tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
        veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
        commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
        velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
        occaecat cupidatat non proident, sunt in culpa qui officia deserunt
        mollit anim id est laborum. Curabitur pretium tincidunt lacus. Nulla
        gravida orci a odio. Nullam varius, turpis et commodo pharetra, est eros
        bibendum elit, nec luctus magna felis sollicitudin mauris. Integer in
        mauris eu nibh euismod gravida.
      </p>
      <button @click="handleShowMore()"><i class="arrow"></i></button>
    </div>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      showMore: false,
    };
  },
  methods: {
    handleShowMore() {
      this.showMore = !this.showMore;
    },
  },
};
</script>

<style>
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
/* Box */
.box {
  margin: 22px auto;
  width: 320px;
  padding: 12px 32px 64px;
  max-height: 162px;
  overflow: hidden;
  transition: max-height 0.3s cubic-bezier(0, 1, 0, 1);
}

.box.open {
  max-height: 100rem;
  transition: max-height 0.3s cubic-bezier(0.9, 0, 0.8, 0.2);
}

/* Text */
@keyframes open {
  from {
    line-clamp: 3;
    -webkit-line-clamp: 3;
    /* opacity: 0; */
  }
  to {
    line-clamp: initial;
    -webkit-line-clamp: initial;
    opacity: 1;
  }
}

@keyframes close {
  from {
    line-clamp: initial;
    -webkit-line-clamp: initial;
    opacity: 1;
  }
  to {
    line-clamp: 3;
    -webkit-line-clamp: 3;
    /* opacity: 0; */
  }
}

.text {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  text-overflow: ellipsis;
  overflow: hidden;
  margin: 12px 0;
  animation: close 2s linear 0.1s forwards;
}
.open .text {
  animation: open 2s linear 0s forwards;
}

/* Irrelevant css... */
.arrow {
  border: solid #000;
  border-width: 0 2px 2px 0;
  display: inline-block;
  padding: 4px;
  transform: rotate(45deg);
  -webkit-transform: rotate(45deg);
}

.open .arrow {
  transform: rotate(-135deg);
  -webkit-transform: rotate(-135deg);
  margin-top: 5px;
}

button {
  background: transparent;
  border: 2px solid #000;
  height: 32px;
  width: 32px;
  border-radius: 50%;
  outline: none;
  cursor: pointer;
  font-size: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto;
}
</style>

Codesandbox:
https://codesandbox.io/s/amazing-goldwasser-e6l95?file=/src/App.vue

Answer №1

Unfortunately, the line-clamp property cannot be animated directly. However, there is a workaround for this issue:

  1. Use ref and the mounted() lifecycle method to calculate the height of the text and store it in the component's data.
  2. Apply the style max-height: 55px to the .text class, which is roughly equivalent to showing 3 lines of text.
  3. Bind the :style attribute to a <p> tag to toggle the height of the text based on a condition.
  4. Add another property to the component's data to handle toggling a custom class and bind it to the box element.
  5. In the handleShowMore() method, include a conditional check that sets a timeout using the CSS transition duration if needed.

https://codesandbox.io/s/vue-how-to-do-a-fade-in-out-for-show-more-function-evj8e

App.vue

<template>
  <div id="app">
    <div class="box" :class="className">
      <div class="top">
        <h1>Show More</h1>
      </div>
      <p
        :style="{ maxHeight: showMore ? expandedTextHeight + 'px' : '' }"
        class="text"
        ref="contentText"
      >
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
        tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
        veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
        commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
        velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
        occaecat cupidatat non proident, sunt in culpa qui officia deserunt
        mollit anim id est laborum. Curabitur pretium tincidunt lacus. Nulla
        gravida orci a odio. Nullam varius, turpis et commodo pharetra, est eros
        bibendum elit, nec luctus magna felis sollicitudin mauris. Integer in
        mauris eu nibh euismod gravida.
      </p>

      <button @click="handleShowMore()"><i class="arrow"></i></button>
    </div>
  </div>
</template>

<script>
export default {
  name: 'App',
  data: () => ({
    showMore: false,
    expandedTextHeight: 0,
    className: '',
  }),
  methods: {
    handleShowMore() {
      this.showMore = !this.showMore;

      if (this.showMore) {
        this.className = 'open';
      } else {
        // Remove class 'open' after .5s delay
        setTimeout(() => (this.className = ''), 500);
      }
    },
  },
  mounted() {
    // Calculate full height of the text
    this.expandedTextHeight = this.$refs.contentText.scrollHeight;
  },
};
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
/* Box */
.box {
  margin: 22px auto;
  width: 370px;
  padding: 12px 32px 64px;
  overflow: hidden;
}

.text {
  max-height: 55px; /* Initial height set to show 3 lines of text */
  display: -webkit-box;
  -webkit-box-orient: vertical;
  text-overflow: ellipsis;
  overflow: hidden;
  margin: 12px 0;
  position: relative;
  line-clamp: 3;
  -webkit-line-clamp: 3;
  transition: all 0.5s cubic-bezier(0.9, 0, 0.8, 0.2);
}

.open .text {
  line-clamp: initial;
  -webkit-line-clamp: initial;
  transition: all 0.5s cubic-bezier(0.9, 0, 0.8, 0.2);
}

/* Other irrelevant CSS styles... */
.arrow {
  border: solid #000;
  border-width: 0 2px 2px 0;
  display: inline-block;
  padding: 4px;
  transform: rotate(45deg);
  -webkit-transform: rotate(45deg);
}

.open .arrow {
  transform: rotate(-135deg);
  -webkit-transform: rotate(-135deg);
  margin-top: 5px;
}

button {
  background: transparent;
  border: 2px solid #000;
  height: 32px;
  width: 32px;
  border-radius: 50%;
  outline: none;
  cursor: pointer;
  font-size: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto;
}
</style>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Reasons for the failure of file uploads from the React frontend to the backend system

Recently, I embarked on a new project that involves using React for the front-end and Node for the back-end. The main requirement of this project is to upload multiple images from the front-end, with the back-end handling the file uploads to Cloudinary. I ...

allowing users to scale content and adjust layouts based on device characteristics

When I inherited the maintenance of a website, I discovered that it was not optimized for mobile devices. The client requested that the site be made accessible on mobile devices, so I attempted to ensure that the mobile version mirrored the desktop version ...

Automating the Process of File Downloads Using Ajax and PHP

I am currently working on a form that requires users to enter a secret key. Once the key is submitted, it is verified against a database. If the key is found, the relevant file associated with the key should be downloaded automatically. I have successfully ...

Using AngularJS in conjunction with other scripts

I am currently working on an application and now I have the task of implementing a dynamic menu using AngularJS. This requires me to modify variables in the AngularJS application from my existing code. Here is the example I am experimenting with: <scr ...

Ensure that bulleted lists and numbered lists are aligned to the left side

Check out this website where the ordered and unordered lists are not aligned correctly. The ideal alignment would have the bullets (or numbers) left aligned. For instance, the number "1." should be aligned to the left on the same line as the heading "Per ...

Solving Angular Circular Dependencies

My popupservice allows me to easily open popup components: export class PopupService { alert() { this.matdialog.open(PopupAlertComponent); } yesno() { this.matdialog.open(PopupYesNoComponent); } custom() { this.matdialog.open(PopupCustomCompon ...

Guide to excluding all subdependencies using webpack-node-externals

My current setup involves using webpack to bundle both server assets and client code by specifying the target property. While this configuration has been working well, I encountered an issue where webpack includes all modules from node_modules even for ser ...

Retrieve Gravatar image using JSON data

I am currently working on extracting data to show a user's Gravatar image. The JSON I have is as follows: . On line 34, there is 'uGava' which represents their gravatar URL. Ideally, it should be combined with / + uGava. Currently, I have ...

Animation that increments to a predetermined value

I'm trying to create a counter animation that dynamically animates a value calculated by the checkboxes selected. The calculation is working fine, but the animation is not happening. http://jsfiddle.net/dbtj93kL/ $('input[type="checkbox"]&apo ...

Which CSS 3 transition prefixes are recommended for optimal performance?

I'm curious about the CSS vendor prefixes needed for transitions. One source mentions that "you need to use all the usual prefixes to make this work in all browsers (-o-, -webkit-, -moz-, -ms-)". Another page only displays the -webkit- and -moz- pre ...

Tips for automatically loading a new page or URL when a user scrolls to the bottom

I am working on implementing infinite scroll functionality, where a new page loads automatically when the user reaches the bottom of the page or a particular div. Currently, I have this code that loads a new page onclick. $("#about").click(function(){ ...

Hide the entire TR if Mysql equals zero

How can I apply the style "display: none;" to an entire TR if the result from row credits is 0? Should I achieve this using CSS or a MySQL query? <?php $username = "root"; $password = ""; $database = "aaa"; $mysqli = ne ...

Shattered raw emotion

Does anyone have any insight on how to resolve this error? I've hit a roadblock trying to figure out the issue in the message below. Here is the snippet of code: :label="` ${$t('cadastros.clientes.edit.status')}: ${cliente.status === ...

Passing along the mouse event to the containing canvas element that utilizes chart.js

Recently, I created a custom tooltip for my chart.js chart by implementing a div that moves above the chart. While it works well, I noticed that the tooltip is capturing mouse events rather than propagating them to the parent element (the chart) for updati ...

What is the correct way to encode an HTML string in JavaScript?

I have identified a XSS Scripting vulnerability in my code and I want to prevent it. To do so, I am utilizing a Jquery Encoder for protection against XSS Scripting attacks. Below is the JavaScript code snippet: function test(response) { $('#test ...

What are the steps to create a hamburger drawer menu that slides out to the right?

After making the modifications specified in the comments labeled 'modify' for this codepen, the result is that the hamburger icon moves to the right and slides from the right as intended. However, I would like the menu content to also slide out ...

Error encountered when attempting to have multiple chrome.runtime.onMessage listeners - port disconnection issue

I am currently developing a chrome-extension that utilizes the Dexie indexedDB Wrapper, various jQuery Libraries, and syncfusion's eGrid to manage and display data. While I know this issue has been addressed multiple times in the past, I have encounte ...

Error: The term "Particles" has not been defined

I'm attempting to integrate code from a website into my project, but encountered an error when the particles failed to run after adding it. I downloaded and installed particle.js from "https://github.com/marcbruederlin/particles.js/issues" for this pu ...

Two objects intersecting in space

When using AngularJS for insert and update operations, I encounter a problem where changes made to user data are reflected in the list of users. Additionally, when adding a new user, the last record's data populates all input fields. Code: User List ...

Hide the top rectangle in vue-chartjs that displays the datasets label

view chart rendering chart appearance inquiry After numerous attempts, I still can't seem to get rid of the persistent red rectangle above my chart. Is there a solution to remove it? It seems that the usual methods for adjusting options and legends, ...