Skip to content

Highlight

<template>
    <div style="height: 100vh">
        <vue-reader url="/vue-book-reader/files/啼笑因缘.epub" :getRendition="getRendition" />
    </div>
    <div class="selections-panel">
        <div class="selections-title">Selections</div>
        <ul class="selections-list">
            <li v-for="({ text, cfi }, index) in selections" :key="index" class="selections-item">
                <span :title="text" class="selections-text">{{ text }}</span>
                <button class="selections-btn" @click="show(cfi)">show</button>
                <button class="selections-btn" @click="remove(index)">remove</button>
            </li>
        </ul>
    </div>
</template>

<script setup lang="ts">
import { VueReader } from 'vue-book-reader'
import { Overlayer } from 'vue-book-reader/dist/overlayer.js'
import { ref } from 'vue'

interface Item {
    cfi: string
    text: string
}

let rendition = null
const selections = ref<Item[]>([])

const getSelectionRange = (sel: Selection) => {
    if (!sel || !sel.rangeCount) return
    const range = sel?.getRangeAt(0)
    if (range.collapsed) return
    return range
}
const getRendition = (val) => {
    rendition = val
    rendition.addEventListener('load', (e) => {
        const { doc, index } = e.detail
        doc.addEventListener('pointerup', () => {
            const sel = doc.getSelection() as Selection
            const range = getSelectionRange(sel)
            if (!range) return
            const cfi = rendition.getCFI(index, range)
            const text = sel.toString()
            const annotation = {
                value: cfi,
                type: 'highlight',
                color: 'red',
                note: text,
            }
            selections.value = [...selections.value, { text, cfi }]
            rendition.addAnnotation(annotation)
        })
    })
    rendition.addEventListener('draw-annotation', (e) => {
        const { draw, annotation } = e.detail
        const { color, type } = annotation
        if (type === 'highlight') draw(Overlayer.highlight, { color })
        else if (type === 'underline') draw(Overlayer.underline, { color })
        else if (type === 'squiggly') draw(Overlayer.squiggly, { color })
    })
}
const show = (cfi: string) => {
    rendition.goTo(cfi)
}
const remove = (index: number) => {
    selections.value = selections.value.filter((i, j) => {
        if (index !== j) {
            return true
        } else {
            const annotation = {
                value: i.cfi,
                note: i.text,
            }
            rendition.addAnnotation(annotation, true)
            return false
        }
    })
}
</script>

<style scoped>
.selections-panel {
    z-index: 1;
    color: #000;
    background-color: #fff;
    border: 1px solid #a8a29e;
}

.selections-title {
    font-weight: bold;
    margin-bottom: 5px;
}

.selections-list {
    border-color: #a8a29e;
}

.selections-item {
    display: flex;
    justify-content: space-between;
}

.selections-text {
    width: 60%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    cursor: pointer;
}

.selections-btn {
    color: #737373;
    text-align: center;
    background-color: #f97316;
    cursor: pointer;
    margin-right: 5px;
    border-radius: 10px;
    padding: 5px
}
</style>