Making Miniature Book with CSS
Tags: css, vue
Published: 2020-07-04 07:43:33

---
title: Making Miniature Book with CSS
published: true
description: 
tags: CSS, vuejs
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/dhmm708zy2l7ok6nxomh.png
---

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/dhmm708zy2l7ok6nxomh.png)

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](https://twitter.com/hashtag/%E9%87%91%E6%9B%9CGUI?src=hashtag_click&f=live).

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

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/12zm6ev5jqzzw1bgv8ao.png)

# 2. Adding a three-dimensional page with perspective properties

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/d7w9v3fgduqq7tzvujkx.gif)

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.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/n6u2noeb6g8b31ijofyb.png)

# 3. Curve the edges of the page

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/tf018n7ks1xxfxanfscy.gif)

CSS can be used to create joint animations. I learned this from [s14garnet](https://codepen.io/YusukeNakaya/).

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/axfe5529vdeiznuch0al.png)

Transformations are affected by the parent element. they can be concatenated using `preserve-3d`.

# 4. Add dragging interactions to continuously turn pages

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/6mb78pcch1286rn2fpo1.gif)

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: 
```js
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.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/9z14gkrxhkpa5bz6iq79.gif)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/eqx3f5heimzea9w04gs9.gif)

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/hmjpygph2zp0pjqfo2zc.gif)

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">
```