markdown的代码块复制插件
# 图例
针对 vuepress 的架构,开发的这个 vue 插件,主要用来复制代码块内的代码

# 实现思路
写一个 vue 组件 CodeCopy ,这个组件构造了一个 vue 实例,内容是一个 svg 图片,就是 copy 的那张图片,图片注册了点击复制的事件
在页面渲染完成后,通过 className 查到到页面上所有的代码块的 dom,再将代码块内的 text 全部提取出来,然后实例化一个 CodeCopy 组件并将代码块的 text 文本传入,将这个实例化后的组件 dom 插入到代码块 dom 的前面,从而这样的一个点击负责的 icon 就实现了
# 实现代码
点击 展开/收起 jsx 代码
/**
* CodeCopy 组件
*/
<template>
<div class="code-copy">
<svg
@click="copyToClipboard"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
:class="iconClass"
:style="alignStyle"
>
<path fill="none" d="M0 0h24v24H0z" />
<path
:fill="options.color"
d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm-1 4l6 6v10c0 1.1-.9 2-2 2H7.99C6.89 23 6 22.1 6 21l.01-14c0-1.1.89-2 1.99-2h7zm-1 7h5.5L14 6.5V12z"
/>
</svg>
<span :class="success ? 'success' : ''" :style="alignStyle">
{{ options.successText }}
</span>
</div>
</template>
<script>
export default {
props: {
parent: Object,
code: String,
options: {
align: String,
color: String,
backgroundTransition: Boolean,
backgroundColor: String,
successText: String,
staticIcon: Boolean,
},
},
data() {
return {
success: false,
originalBackground: null,
originalTransition: null,
};
},
computed: {
alignStyle() {
let style = {};
style[this.options.align] = '7.5px';
return style;
},
iconClass() {
return this.options.staticIcon ? '' : 'hover';
},
},
mounted() {
this.originalTransition = this.parent.style.transition;
this.originalBackground = this.parent.style.background;
},
beforeDestroy() {
this.parent.style.transition = this.originalTransition;
this.parent.style.background = this.originalBackground;
},
methods: {
hexToRgb(hex) {
let result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result
? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16),
}
: null;
},
copyToClipboard(el) {
if (!navigator.clipboard) {
let inputEle = document.getElementById('clipboard');
if (!inputEle) {
inputEle = document.createElement('input');
inputEle.id = 'clipboard';
document.body.appendChild(inputEle);
}
inputEle.setAttribute('value', content);
inputEle.style.display = 'block';
if (inputEle && inputEle.select) {
inputEle.select();
try {
document.execCommand('copy');
this.setSuccessTransitions();
} catch (err) {
console.error(err);
}
}
inputEle.style.display = 'none';
} else {
navigator.clipboard
.writeText(this.code)
.then(() => {
this.setSuccessTransitions();
})
.catch((err) => {});
}
},
setSuccessTransitions() {
clearTimeout(this.successTimeout);
if (this.options.backgroundTransition) {
this.parent.style.transition = 'background 350ms';
let color = this.hexToRgb(this.options.backgroundColor);
this.parent.style.background = `rgba(${color.r}, ${color.g}, ${color.b}, 0.1)`;
}
this.success = true;
this.successTimeout = setTimeout(() => {
if (this.options.backgroundTransition) {
this.parent.style.background = this.originalBackground;
this.parent.style.transition = this.originalTransition;
}
this.success = false;
}, 500);
},
},
};
</script>
<style scoped>
svg {
position: absolute;
right: 7.5px;
opacity: 0.75;
cursor: pointer;
}
svg.hover {
opacity: 0;
}
svg:hover {
opacity: 1 !important;
}
span {
position: absolute;
font-size: 0.85rem;
line-height: 0.425rem;
right: 50px;
opacity: 0;
transition: opacity 500ms;
}
.success {
opacity: 1 !important;
}
</style>
/**
* clientRootMixin
*/
import Vue from 'vue';
import CodeCopy from './CodeCopy.vue';
import './style.css';
export default {
updated() {
this.update();
},
methods: {
update() {
setTimeout(() => {
document.querySelectorAll(selector).forEach((el) => {
if (el.classList.contains('code-copy-added')) return;
let ComponentClass = Vue.extend(CodeCopy);
let instance = new ComponentClass();
let options = {
align: align,
color: color,
backgroundTransition: backgroundTransition,
backgroundColor: backgroundColor,
successText: successText,
staticIcon: staticIcon,
};
instance.options = { ...options };
// 获取 el 下的第一个 code 标签
const codeElement = el.querySelector('code');
// 如果存在 code 标签,获取其文本内容,否则的话直接获取el下的文本内容
const textContent = codeElement
? codeElement.textContent
: el.textContent;
instance.code = textContent;
instance.parent = el;
instance.$mount();
el.classList.add('code-copy-added');
// el.appendChild(instance.$el);
el.insertBefore(instance.$el, codeElement);
});
}, 100);
},
},
};
← svg和png转base64插件 提交脚本 →