问题描述
我正在尝试在Vue组件中使用CSS蒙版。我需要完成下面的toSvg
函数的实现。这样会将Vue VNode从this.$slots.default
转换为SVG字符串。
<script>
export default {
computed: {
maskImage() {
const svg = this.toSvg(this.$slots.default);
const encodedSvg = btoa(svg);
return `url('data:image/svg+xml;base64,${encodedSvg}')`;
},},methods: {
toSvg(vnode) {
// Todo: How can I convert the VNode to a string like the one below?
// In React,I Could use const svg = ReactDOMServer.renderToStaticmarkup(vnode);
return `<svg viewBox="0 0 260 68" xmlns="http://www.w3.org/2000/svg">
<rect x="80" y="32" width="160" height="12" rx="2" />
<rect width="180" height="20" rx="2" />
<rect x="80" y="52" width="95" height="12" rx="2" />
<rect y="26" width="68" height="42" rx="2" />
</svg>`;
},render(createElement) {
return createElement("div",{
attrs: {
class: "skeleton",style: `-webkit-mask-image: ${this.maskImage}; mask-image: ${this.maskImage};`,});
},};
</script>
<style lang="scss">
.skeleton {
animation: skeleton-animation 2s infinite linear;
background: linear-gradient(to right,hsl(30,1,99) 0%,2,95) 30%,95) 70%,99) 100%) 0 0 / 200% 100% hsl(30,95);
overflow: hidden;
position: relative;
width: 200px;
height: 100px;
@keyframes skeleton-animation {
100% {
background-position: -200% 0;
}
}
}
</style>
用法示例:
<u-skeleton>
<svg viewBox="0 0 260 68" xmlns="http://www.w3.org/2000/svg">
<rect x="80" y="32" width="160" height="12" rx="2" />
<rect width="180" height="20" rx="2" />
<rect x="80" y="52" width="95" height="12" rx="2" />
<rect y="26" width="68" height="42" rx="2" />
</svg>
</u-skeleton>
显示为:
解决方法
使用Vue.extend
构造一个SVG组件 constructor ,在构造函数的render function
内部,它呈现slots.default
。
下一步是创建其实例,然后创建mount()
并获取已编译的html。
Vue.component('v-test',{
computed: {
maskImage() {
let vnodes = this.$slots.default
let SVGConstructor = Vue.extend({
render: function (h,context) {
return h('svg',{
attrs: {
xmlns: 'http://www.w3.org/2000/svg'
}
},vnodes)
}
})
let instance = new SVGConstructor()
instance.$mount()
const encodedSvg = btoa(instance.$el.outerHTML);
return `url('data:image/svg+xml;base64,${encodedSvg}')`;
}
},render(createElement) {
return createElement("div",{
attrs: {
class: "skeleton",style: `-webkit-mask-image: ${this.maskImage}; mask-image: ${this.maskImage};`,},})
},})
new Vue({
el: '#app'
})
.skeleton {
animation: skeleton-animation 2s infinite linear;
background: linear-gradient(to right,#fcfcfc 0%,#f3f2f2 30%,#f3f2f2 70%,#fcfcfc 100%) 0 0 / 200% 100% #f3f2f2;
overflow: hidden;
position: relative;
width: 200px;
height: 100px;
}
@keyframes skeleton-animation {
100% {
background-position: -200% 0;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<v-test>
<svg viewBox="0 0 260 68" xmlns="http://www.w3.org/2000/svg">
<rect x="80" y="32" width="160" height="12" rx="2" />
<rect width="180" height="20" rx="2" />
<rect x="80" y="52" width="95" height="12" rx="2" />
<rect y="26" width="68" height="42" rx="2" />
</svg>
</v-test>
<hr>
<v-test>
<svg viewBox="0 0 260 68" x="0" y="0" xmlns="http://www.w3.org/2000/svg">
<rect x="80" y="32" width="160" height="12" rx="2" />
<rect width="180" height="20" rx="2" />
<rect x="80" y="52" width="95" height="12" rx="2" />
<rect y="26" width="68" height="42" rx="2" />
</svg>
<svg viewBox="0 0 260 68" x="20" y="-20" xmlns="http://www.w3.org/2000/svg">
<rect x="80" y="32" width="160" height="12" rx="2" />
<rect width="180" height="20" rx="2" />
<rect x="80" y="52" width="95" height="12" rx="2" />
<rect y="26" width="68" height="42" rx="2" />
</svg>
</v-test>
</div>