はじめに
初めて需要を受け、一般的なことなので、誰かがすでにライブラリを構築している必要があり、右、急いでgayhubで見に行くと、確かにある - ビュー-ツアー。
公式のエフェクトを見てみると、バブルコンポーネントのような見た目で、エフェクトもapiも中々です。
しかし、我々のuiはもっとチャラチャラしているし、カスタムコンテンツもあるし、ギャップはかなり大きい。
アイデア
スクリーンマスクレイヤー上のあるエリアを強調したい場合、元の位置に全く同じスタイルのdom要素をカバーする必要があり、カスタムコンテンツはこのエリアを囲む必要があります。その場合、必要なパーツは以下の通りです:
- ハイライトする要素の座標情報
要素自身の幅と高さ、ブラウザのビューポートの上端と左端からの距離を取得します。
- ハイライトされた要素のコピー
ここでcloneNodeを使ってdomを操作するのは確かに適切ではありませんし、最終的なスタイルが反映されるとは限りません。
html2canvas(document.body).then(function(canvas) {
// base64イメージを生成する
let img = canvas.toDataURL('image/png')
});
コンポーネントデザイン
実装コード
- 座標の取得
// xx.vue
//要素の垂直座標を取得する
getTop(e) {
let offset = e.offsetTop
if (e.offsetParent !== null) offset += this.getTop(e.offsetParent)
return offset
},
//要素の水平座標を取得する
getLeft(e) {
let offset = e.offsetLeft
if (e.offsetParent !== null) offset += this.getLeft(e.offsetParent)
return offset
},
getImgBase64 (idStr) {
let _el = document.querySelector(`#${idStr}`)
let style = window.getComputedStyle(_el)
//
this.width = parseInt(style.width)
//
this.height = parseInt(style.height)
// トップ距離
this.offsetTop = vm.getTop(_el)
// 左の距離
this.offsetLeft = vm.getLeft(_el)
}
HDスクリーンショットの生成
// xx.vue
getImgBase64(idStr) {
let vm = this
let _canvas = document.createElement('canvas')
let _el = document.querySelector(`#${idStr}`)
let style = window.getComputedStyle(_el)
this.width = parseInt(style.width)
this.height = parseInt(style.height)
let context = _canvas.getContext('2d')
//以下のコードは、画面の解像度に応じて、キャンバスの幅と高さを設定し、マルチスクリーンに対応した高精細なイメージを得るためのものだ。
// 画面のデバイスピクセル比
let devicePixelRatio = window.devicePixelRatio || 2
// ブラウザがキャンバスをレンダリングし、キャンバスの情報をピクセル比率で保存する。
let backingStoreRatio =
context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1
// canvas実際のレンダリング倍率は
let ratio = devicePixelRatio / backingStoreRatio
_canvas.width = this.width * ratio
_canvas.height = this.height * ratio
_canvas.style.width = this.width + 'px'
_canvas.style.height = this.height + 'px'
html2canvas(_el, {
canvas: _canvas
}).then(function (canvas) {
// 写真はこちら
let imgData = canvas.toDataURL('image/png')
})
}
このメソッドを実行する前に、親コンポーネントの非同期データが終了し、domが更新されるまで待つ必要があることに注意してください。
this.$nextTick(() =>{
this.getImgBase64('id')
})
解決すべき問題
- ブラウザが伸びると、ハイライト領域がその動きに追従します。
created() {
window.addEventListener('resize', this.resetPos)
},
beforeDestroy() {
window.removeEventListener('resize', this.resetPos)
},
methods: {
resetPos() {
let vm = this
let _el = document.querySelector(`#${vm.idStr}`)
let style = window.getComputedStyle(_el)
this.width = parseInt(style.width)
this.height = parseInt(style.height)
this.offsetTop = vm.getTop(_el)
this.offsetLeft = vm.getLeft(_el)
}
}
- ユーザーガイドラインが表示される場合
操作時間をローカルにキャッシュし、1週間など適切な間隔を独自に決定することで、操作できます。
- 親コンポーネントの呼び出しを容易にする方法
表示のタイミングは、コンポーネント内のopenメソッドとcloseメソッドで制御します。
コンポーネントのコード一式
<template>
<section class="tips-dialog" v-show="showBtn">
<span class="bg"></span>
<div class="target" :style="posStyle">
<img v-imgErr @click="close" :src="domImg" />
<div class="content" :style="`${pos === 'left' ? 'right' : 'left'}:${width}px`">
<slot name="content"></slot>
</div>
</div>
</section>
</template>
<script>
import html2canvas from 'html2canvas'
export default {
name: 'tips-dialog',
data() {
return {
// 要素ノードのスクリーンショット
domImg: '',
// スイッチを表示する
showBtn: false,
offsetTop: 0,
offsetLeft: 0,
width: 0,
height: 0
}
},
props: {
idStr: {
type: String,
required: true
},
pos: {
type: String,
default: () => {
return 'left'
}
}
},
watch: {
idStr() {
this.getImgBase64()
}
},
computed: {
posStyle() {
return `left:${this.offsetLeft}px;top:${this.offsetTop}px;width:${this.width}px;height:${this.height}px;`
}
},
created() {
window.addEventListener('resize', this.resetPos)
},
beforeDestroy() {
window.removeEventListener('resize', this.resetPos)
},
methods: {
//要素の垂直座標を取得する
getTop(e) {
let offset = e.offsetTop
if (e.offsetParent !== null) offset += this.getTop(e.offsetParent)
return offset
},
//要素の水平座標を取得する
getLeft(e) {
let offset = e.offsetLeft
if (e.offsetParent !== null) offset += this.getLeft(e.offsetParent)
return offset
},
open() {
!this.domImg && this.getImgBase64()
if (this.domImg) {
this.showBtn = true
}
},
close() {
this.showBtn = false
this.$emit('close')
},
resetPos() {
let vm = this
let _el = document.querySelector(`#${vm.idStr}`)
let style = window.getComputedStyle(_el)
this.width = parseInt(style.width)
this.height = parseInt(style.height)
this.offsetTop = vm.getTop(_el)
this.offsetLeft = vm.getLeft(_el)
},
getImgBase64() {
let vm = this
let _canvas = document.createElement('canvas')
let _el = document.querySelector(`#${vm.idStr}`)
let style = window.getComputedStyle(_el)
let w = parseInt(style.width)
this.width = w
let h = parseInt(style.height)
this.height = h
this.offsetTop = vm.getTop(_el)
this.offsetLeft = vm.getLeft(_el)
//ニーズに応じて、オフセットを参照するコンテキスト・パラメータを変更することができる。
let context = _canvas.getContext('2d')
//以下のコードは、画面の解像度を取得し、キャンバスの幅と高さを設定して、高解像度のイメージを取得するものだ。
// 画面のデバイスピクセル比
let devicePixelRatio = window.devicePixelRatio || 2
// ブラウザがキャンバスをレンダリングし、キャンバスの情報をピクセル比率で保存する。
let backingStoreRatio =
context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1
// canvas実際のレンダリング倍率は
let ratio = devicePixelRatio / backingStoreRatio
_canvas.width = w * ratio
_canvas.height = h * ratio
_canvas.style.width = w + 'px'
_canvas.style.height = h + 'px'
html2canvas(_el, {
canvas: _canvas
}).then(function (canvas) {
vm.domImg = canvas.toDataURL('image/png')
let imgEl = document.createElement('img')
imgEl.onload = function () {
setTimeout(() => {
vm.showBtn = true
}, 20)
}
imgEl.setAttribute('src', vm.domImg)
})
}
}
}
</script>
<style lang="stylus" scoped>
.tips-dialog
position fixed
top 0
left 0
width 100%
height 100%
z-index 1001
.bg
position relative
display block
width 100%
height 100%
background rgba(0, 0, 0, 0.5)
.target
position absolute
img
display block
width 100%
height 100%
cursor pointer
.content
position absolute
top 50%
</style>
コンポーネントの使い方
<!-- HTML -->
<tips-dialog idStr="domId" ref="tipsDialog" pos="left">
<template v-slot:content>
<div class="user-explan">
<!-- 独自のユーザーガイドコンテンツ>
</div>
</template>
</tips-dialog>
<!-- js -->
// asyncの最後に
this.$nextTick(() => {
// オープンユーザガイドライン
this.$refs.tipsDialog.open()
})
<!-- CSS -->
// スロットのコンテンツは、ポジション・オフセットのスタイルを補完することを忘れてはならない。
この時点で、比較的単純なライトボックス・ブートストラップ・コンポーネントが実装されました。しかし、まだ拡張の余地があります。
- ユーザーガイドに複数のステップがある場合は?
すでに0から1を達成したように、1から100まで、原理は同じですが、需要がない限り、私はこのコンポーネントを拡大し続けるべきではありませんが、いくつかのより多くの髪や香。
- カスタムコンテンツは、スタイルの書き込みを減らすために自動配置することができますか?
それはvue-tourと同じであることができ、単純なニーズを満たすために、正規の先端テンプレートとapiのセットがコンポーネント内で提供されています。
カスタマイズに関しては、要件は様々なので、自分で書いた方が良いでしょう。