JSFiddle - React, Tailwind, and code Playground

by kurotanshi

HTML

<script src="https://unpkg.com/vue@next"></script>
<div id="app">

  <div class="flexbox-wrapper" :style="{height: height+'px'}">
    <div class="flexbox-body" ref="content">
      <div class="user-block" v-if="userInfo.name">
        <h2>{{ userInfo.name }} / @{{ userInfo.username }}</h2>
        
        <img src="https://dummyimage.com/200x200/666/fff" alt="dummyimage">
        <div class="wraps">
          <p>{{ userInfo.company.name }}</p>
          <p>{{ userInfo.phone }}</p>
          <p>{{ userInfo.email }}</p>
        </div>
      </div>
    </div>

  </div>


  <transition @before-enter="beforeEnter" @before-leave="beforeLeave">
    <div v-if="isLoading" class="loading">
      <img src="https://raw.githubusercontent.com/kurotanshi/vue-functional-demo/master/loading.gif" alt="loading">
    </div>
  </transition>

  <button @click="getUserInfo">取得隨機 User 資訊</button>
</div>

SCSS

#app {
  position: relative;
  display: block;
  padding: 1rem;
  font-size: 1rem;
}

.flexbox-wrapper {
  position: relative;
  overflow: hidden;
  transition: height 1s;
  min-height: 20px;
  height: auto;
  margin-bottom: 1rem;
}

.flexbox-body {
  font-size: 1rem;
  line-height: 1.75;
}

.user-block {
  overflow: hidden;
  background: #eee;
  padding: 1rem;
  margin-bottom: 1rem;
  width: 420px;
  
  img {
    width: 100px;
    height: 100px;
    float: left;
    margin: 0 1rem 0 0;
  }
  
  .wraps{    
    float: left;
    margin-top: 1rem;
    margin-left: 0.7rem;
    font-size: 0.9rem;
  }
  
  h2 {
    font-weight: 500;
    margin: 0 0 0.5rem 0;
    font-size: 1rem;
  }
}

.loading {
  display: block;
  margin: 0 auto;
  text-align: center;

  img {
    border: none;
  }
}

.v-enter-active, .v-leave-active {
  transition: opacity .7s;
}
.v-enter-from, .v-leave-to {
  opacity: 0;
}

JavaScript

const app = Vue.createApp({
  data() {
    return {
      height: 0,
      userInfo: { },
      isLoading: false
    }
  },
  methods: {
  	getRandomUserId () {
    	// 1 ~ 10
    	return Math.floor(Math.random() * 10) + 1;
    },
    async getUserInfo() {
    	this.isLoading = true;
      this.userInfo = { };
      const userId = this.getRandomUserId();
      
      const res = await fetch('https://jsonplaceholder.typicode.com/users/' + userId)
        .then((response) => response.json())
        .then((json) => json);

			// 加上延遲,避免太快看不到 loading
			window.setTimeout(() => {
	    	this.isLoading = false;
	      this.userInfo = res;
      }, 3000);
      
    },
    beforeEnter() {
      this.height = 0;
    },
    beforeLeave() {
      // nextTick
      this.$nextTick(() => {
        // $refs
        this.height = this.$refs.content.getBoundingClientRect().height;
      })
    },
  },
});

app.mount('#app');