最近想在hexo中嵌入一些shader,折腾了一些时间后终于完善,实际上用这种方法不仅可以在hexo中嵌入shader,也可以嵌入babylonjs,pxixjs,Layabox,Egret,Cocos2等,先看效果,原理什么的其实很简单。示例源码
由于一些shader特别消耗显卡性能,在glsl_snippets.js
中判定如果第一帧渲染时间超过0.4秒就不再渲染了。
也可以点击shader暂停渲染
嵌入shader
shader来源shaderToy
完全支持shadertoy的代码,参考自大神的代码stackoverflow,在这位大神的代码里获取到完全兼容shaderToy的思路。并将其改成更适用在hexo中。
示例代码
1 2 3 4 5 6 7 8 9 10
| <div id="three"></div> <script type="module" id="threeMain"> if (!(self.frameElement && self.frameElement.tagName == "IFRAME")) { import("http://localhost:4000/uploads/createTHREE.js").then((result) => result.initHexoThreeModule(document.getElementById("three"),document.getElementById("threeMain"))); } else { import('http://localhost:4000/uploads/glsl_snippets.js').then(async res=>res.glsl_snippets(res.anotherGlsl)) } </script>
|
测试shader效果
嵌入threejs3D场景
实现原理
原因就是hexo对md文件中的script会渲染到页面上,但是不会显示出来,这就有充足的操作空间了。
这里使用iframe的主要原因就是防止来回切换页面导致的webgl上下文问题。不然也不至于这么麻烦。
1 2 3 4 5 6 7 8 9 10 11 12
| function createIframe(divNode) { return new Promise((resolve) => { let iframe = document.createElement("iframe"); iframe.style = "position: absolute; width: 100%; height: 100%; left: 0; top: 0;border:none;"; iframe.onload = () => resolve(iframe.contentWindow); divNode.style = "position: relative; width: 100%; height: 0; padding-bottom: 75%;"; divNode.appendChild(iframe); }); }
|
创建完iframe后可以为iframe中加载对象了,之前使用的是经典前端的script src加载方式,考虑到可能会被用到,这里保留了函数方便后续修改。
实际使用中利用module中的import()
函数直接引入在线文件即可
1 2 3 4 5 6 7 8 9 10 11
| function cdnLoadTHREE(divNode) { return createIframe(divNode).then((iframe_window) => { let link = document.createElement('link') link.href = createCss() link.rel = 'stylesheet' link.type = 'text/css' iframe_window.document.head.appendChild(link); return new Promise((resolve)=>resolve(iframe_window)); }); }
|
最后将整个script标签复制到iframe内,代码会在复制完后立即执行,由于代码已经在iframe内了,所以window也指向了iframe中,这一步才使得可以方便的使用module保证了向前兼容的同时,也能对老式的代码向下兼容。不至于出现一些奇奇怪怪的问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| function initHexoThreeModule(divNode,scriptNode) { let link = document.createElement("link"); link.href = createCss(); link.rel = "stylesheet"; link.type = "text/css"; document.head.appendChild(link); if(divNode && scriptNode){ cdnLoadTHREE(divNode).then((iframe_window)=>{ let script = document.createElement('script') script.src = createBlob(scriptNode.text,'application/javascript') iframe_window.document.head.appendChild(script) }) } }
|
参考和推荐