Last active
February 25, 2019 15:24
-
-
Save AdmiralPotato/9c30115ce525c3f6d2dd602cd82f42cd to your computer and use it in GitHub Desktop.
Chrome has bad performance rendering SVG circles
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<title>Chrome has bad performance rendering SVG circles</title> | |
<meta http-equiv="Content-type" content="text/html; charset=utf-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1, minimal-ui, maximum-scale=1, user-scalable=no"> | |
<style> | |
html, body { | |
height: 100%; | |
} | |
body { | |
padding: 0; | |
margin: 0; | |
background-color: #000; | |
color: #fff; | |
font-family: sans-serif; | |
} | |
* { | |
margin: 0; | |
} | |
pre { | |
padding: 1em; | |
} | |
body svg { | |
position: absolute; | |
display: block; | |
width: 100%; | |
height: 100%; | |
top: 0; | |
left: 0; | |
} | |
body svg * { | |
stroke: #fff; | |
} | |
body .overlay { | |
position: relative; | |
z-index: 2; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="target"> | |
<svg viewBox="-1 -1 2 2" class="main-view"> | |
<component | |
v-for="item in items" | |
:key="item.i" | |
:is="type" | |
:args="item" | |
></component> | |
</svg> | |
<div class="overlay"> | |
<pre>Type: {{type}}, FPS: {{fps}}</pre> | |
<button @click="type = 'perfest-circle'">Circle</button> | |
<button @click="type = 'perfest-path-transform'">Path Tranform</button> | |
<button @click="type = 'perfest-path-data'">Path Data</button> | |
<select v-model.number="count"> | |
<option>4</option> | |
<option>5</option> | |
<option>6</option> | |
<option>7</option> | |
<option>8</option> | |
</select> | |
</div> | |
</div> | |
<script src="https://unpkg.com/[email protected]/dist/vue.js"></script> | |
<script> | |
Vue.component('perfest-circle', { | |
props: ['args'], | |
template: ` | |
<circle | |
:cx="args.x" | |
:cy="args.y" | |
:r="args.r" | |
:stroke-width="args.strokeWidth" | |
/> | |
` | |
}) | |
Vue.component('perfest-path-transform', { | |
props: ['args'], | |
template: ` | |
<path | |
:strokeWidth="args.inverseStrokeWidth" | |
:transform="transforms" | |
d="M0-1c-0.5522461,0-1,0.4477539-1,1s0.4477539,1,1,1s1-0.4477539,1-1S0.5522461-1,0-1z M0.211792,0.211792" | |
/> | |
`, | |
computed: { | |
transforms: function() { | |
const transforms = [ | |
'translate(' + this.args.x + ', ' + this.args.y + ')', | |
'scale(' + this.args.r + ')' | |
] | |
return transforms.join('') | |
} | |
} | |
}) | |
var shape = ` | |
M 0, -1 | |
c -0.5522461, 0, -1, 0.4477539, -1, 1 | |
s 0.4477539, 1, 1, 1 | |
s 1, -0.4477539, 1, -1 | |
S 0.5522461, -1, 0, -1 | |
z | |
` | |
var shapeChunks = shape.split('\n').map((line) => { | |
var chunks = line.split(/[,]*\s+/g) | |
chunks.shift() | |
chunks = chunks.map(function (item, index) { | |
var result = item | |
if (index !== 0) { | |
result = parseFloat(item) | |
} | |
return result | |
}) | |
console.log('chunks', chunks) | |
return chunks | |
}) | |
Vue.component('perfest-path-data', { | |
props: ['args'], | |
template: ` | |
<path | |
:transform="transforms" | |
:strokeWidth="args.strokeWidth" | |
:d="pathInstructions" | |
/> | |
`, | |
computed: { | |
transforms: function() { | |
const transforms = [ | |
'translate(' + this.args.x + ', ' + this.args.y + ')' | |
] | |
return transforms.join('') | |
}, | |
pathInstructions: function() { | |
var r = this.args.r | |
return shapeChunks.map(function (chunks) { | |
return chunks.map(function (item, index) { | |
var result = item | |
if (index !== 0) { | |
result = item * r * 0.125 | |
} | |
return result | |
}).join(' ') | |
}).join(' ') | |
} | |
} | |
}) | |
var app = new Vue({ | |
data: { | |
time: 0, | |
type: 'perfest-path-transform', | |
fps: 0, | |
count: 5, | |
strokeWidth: 0.1 | |
}, | |
el: '#target', | |
created: function() { | |
this.animate(0) | |
}, | |
computed: { | |
items: function() { | |
var result = [] | |
var total = Math.pow(this.count, 2) | |
var row = this.count | |
var frac = 1 / row | |
var offset = -1 + (frac) | |
for (var i = 0; i < total; i += 1) { | |
var pulse = (Math.cos((this.time / 1000) + i * frac) + 1) * 0.5 | |
var radius = (pulse * 0.05) + 0.06 | |
result.push({ | |
i: i, | |
x: (((i % row) * frac) * 2) + offset, | |
y: ((Math.floor(i * frac) / row) * 2) + offset, | |
r: radius, | |
strokeWidth: this.strokeWidth, | |
inverseStrokeWidth: (1 - pulse) * this.strokeWidth | |
}) | |
} | |
return result | |
} | |
}, | |
methods: { | |
animate: function(time) { | |
window.requestAnimationFrame(this.animate) | |
this.fps = 1000 / (time - this.time) | |
this.time = time | |
} | |
} | |
}) | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment