As a front-end exercise, I've made a habit of making UI components every Friday.
You can see some of the components I've made in the past in the tag #金曜GUI.
I like small, cute things like miniatures, and yesterday I made a Miniature Book component with CSS and Vue.js.
1. Place some divs at absolute
2. Adding a three-dimensional page with perspective properties
To rotate the div in 3D, use the perspective and transform-style properties.
wrapper:
  perspective: 300px;
  transform-style: preserve-3d;
item:
  transform-origin: left center;
  transform: rotateY(30deg);
You can use transform-origin and position: absolute to adjust the position.
3. Curve the edges of the page
CSS can be used to create joint animations. I learned this from s14garnet.
Transformations are affected by the parent element. they can be concatenated using preserve-3d.
4. Add dragging interactions to continuously turn pages
The implementation of drag interaction is straightforward: just keep track of the difference in offsetX and reflect it in the rotation angle.
You can use modulo to loop the book, but note that JavaScript modulo can return a negative value.
HTML:
<div id="app"
       @pointerdown="down"
       @pointermove="move"
       @pointerup="up">
  ...
</div>
JavaScript:
Number.prototype.mod = function (n) {
  return ((this % n) + n) % n;
};
new Vue({
  el: "#app",
  data: {
    rotateRaw: 40,
    rotateOld: 0,
    offset: null
  },
  methods: {
    down(ev) {
      this.$el.setPointerCapture(ev.pointerId);
      this.offset = ev.offsetX;
      this.rotateOld = this.rotate;
    },
    up(ev) {
      this.offset = null;
    },
    move(ev) {
      if (this.offset) {
        this.rotateRaw = this.rotateOld - (ev.offsetX - this.offset);
      }
    }
  },
  computed: {
    rotate() {
      return this.rotateRaw.mod(180);
    }
  }
});
Note that the pointerdown, pointermove and pointerup events require a polyfill in iOS Safari.
To prevent the div element in the wrapper from being selected by dragging, use pointer-events: none to make the Pointer events transparent.
.wrapper {
  ...
  pointer-events: none;
  user-select: none;
}
Here's DEMO:
https://codepen.io/hashrock/pen/RwrxeKe
Vue.js and input type="range" are your friends
I like to adjust the look and feel of the UI with input type="range". It's a one off UI editor. It's similar to building scaffolding to build a house.
input type="range" is useful, but don't forget to write v-model.number to convert its value to a number.
<input type="range" min=0 max=180 step=1 v-model.number="rotate">
              









    
Top comments (1)
Cute components! I'm impressed how relatively little code and is required to implement and even customize it