├── .env ├── .github └── workflows │ └── docker-image.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── babel.config.js ├── jsconfig.json ├── package.json ├── public ├── css │ └── main.css ├── download.html ├── favicon.ico ├── favicon.png ├── index.html ├── js │ ├── 3d-lines-animation.js │ ├── canvas-renderer.js │ ├── color.js │ ├── djtx.min.js │ ├── jquery.min.js │ ├── projector.js │ └── three.min.js └── web │ ├── netflix.svg │ └── nf.ico ├── src ├── App.vue ├── assets │ └── css │ │ ├── dark.min.css │ │ ├── element-ui.scss │ │ ├── element-variables.scss │ │ └── light.min.css ├── components │ └── SvgIcon │ │ └── index.vue ├── icons │ ├── index.js │ └── svg │ │ ├── bilibili.svg │ │ ├── github.svg │ │ ├── telegram.svg │ │ └── youtube.svg ├── main.js ├── plugins │ ├── axios.js │ ├── base64.js │ ├── clipboard.js │ ├── device.js │ ├── element-ui.js │ └── particles.js ├── router │ └── index.js └── views │ └── Subconverter.vue ├── vue.config.js └── yarn.lock /.env: -------------------------------------------------------------------------------- 1 | VUE_APP_PROJECT = "https://github.com/youshandefeiyang/sub-web-modify" 2 | 3 | VUE_APP_BOT_LINK = "https://t.me/feiyangdigital" 4 | 5 | VUE_APP_BILIBILI_LINK = "https://space.bilibili.com/138129883" 6 | 7 | VUE_APP_YOUTUBE_LINK = "https://youtube.com/channel/UCKHJ2UPlkNsDRj1cVXi0UsA" 8 | 9 | VUE_APP_BASIC_VIDEO = "https://www.youtube.com/watch?v=C4WV4223uYw" 10 | 11 | VUE_APP_ADVANCED_VIDEO = "https://www.youtube.com/watch?v=cHs-J2P5CT0" 12 | 13 | VUE_APP_SCRIPT_CONFIG = "https://github.com/tindy2013/subconverter/blob/a24cb7c00a7e5a71ef2e6c0d64d84d91bc7a21d6/README-cn.md?plain=1#L703-L719" 14 | 15 | VUE_APP_FILTER_CONFIG = "https://github.com/tindy2013/subconverter/blob/a24cb7c00a7e5a71ef2e6c0d64d84d91bc7a21d6/README-cn.md?plain=1#L514-L531" 16 | 17 | VUE_APP_SUBCONVERTER_REMOTE_CONFIG = "https://subconverter.oss-ap-southeast-1.aliyuncs.com/Rules/RemoteConfig/universal/urltest.ini" 18 | 19 | # API 后端 20 | VUE_APP_SUBCONVERTER_DEFAULT_BACKEND = "https://url.v1.mk" 21 | 22 | # 短链接后端 23 | VUE_APP_MYURLS_DEFAULT_BACKEND = "https://v1.mk" 24 | 25 | # 文本托管后端 26 | VUE_APP_CONFIG_UPLOAD_BACKEND = "https://subapi.v1.mk" 27 | -------------------------------------------------------------------------------- /.github/workflows/docker-image.yml: -------------------------------------------------------------------------------- 1 | name: Docker Image CI 2 | on: 3 | push: 4 | branches: 5 | - master 6 | 7 | jobs: 8 | build-and-push: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Check out repository 13 | uses: actions/checkout@master 14 | 15 | - name: Set up Docker Buildx 16 | uses: docker/setup-buildx-action@v2 17 | 18 | - name: Log in to Docker Hub 19 | uses: docker/login-action@v2 20 | with: 21 | username: ${{ secrets.DOCKERHUB_USERNAME }} 22 | password: ${{ secrets.DOCKERHUB_TOKEN }} 23 | 24 | - name: Build and push Docker image 25 | uses: docker/build-push-action@v4 26 | with: 27 | context: . 28 | platforms: linux/amd64,linux/arm,linux/arm64 29 | push: true 30 | tags: youshandefeiyang/sub-web-modify:latest 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-alpine AS build 2 | WORKDIR /app 3 | COPY . . 4 | RUN yarn install 5 | RUN yarn build 6 | 7 | FROM nginx:1.24-alpine 8 | COPY --from=build /app/dist /usr/share/nginx/html 9 | EXPOSE 80 10 | CMD [ "nginx", "-g", "daemon off;" ] 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 youshandefeiyang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sub-web-modify 2 | [本项目](https://suburl.v1.mk)重制[原项目](https://github.com/CareyWang/sub-web)CSS样式,兼容nodejs最新版本(可直接一键部署至Vercel),解决大部分布局细节问题,增加“暗黑模式”,默认自动切换亮/暗模式(点击“太阳/月亮”图标可手动切换),增加“高级功能”点击显示/隐藏,添加短链接选择/自定义功能,增加近百条远程配置,新增[sub-web聚合API](https://github.com/youshandefeiyang/sub-web-api),增加从短链接中获取订阅信息并返回至前端界面,增加上传自定义远程配置/JS进阶排序节点/JS进阶筛选节点等功能,感兴趣的朋友可以自建API服务,增加URL传参设置自定义后端
3 | ## 效果预览: 4 | ![avatar](https://raw.githubusercontent.com/youshandefeiyang/webcdn/main/sub-web-modify.GIF) 5 | ### 使用方法: 6 | 建议使用Docker一键部署: 7 | ``` 8 | docker run -d --restart unless-stopped --privileged=true -p 8090:80 --name sub-web-modify youshandefeiyang/sub-web-modify 9 | ``` 10 | 访问地址举例: 11 | ``` 12 | http://192.168.10.1:8090/?backend=https://url.v1.mk 13 | ``` 14 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "esnext", 5 | "baseUrl": "./", 6 | "moduleResolution": "node", 7 | "paths": { 8 | "@/*": [ 9 | "src/*" 10 | ] 11 | }, 12 | "lib": [ 13 | "esnext", 14 | "dom", 15 | "dom.iterable", 16 | "scripthost" 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sub-web-modify", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build" 8 | }, 9 | "dependencies": { 10 | "atob": "^2.1.2", 11 | "axios": "^1.7.2", 12 | "btoa": "^1.2.1", 13 | "core-js": "^3.37.1", 14 | "element-ui": "^2.15.14", 15 | "vue": "^2.7.16", 16 | "vue-clipboard2": "^0.3.3", 17 | "vue-router": "^3.6.5" 18 | }, 19 | "devDependencies": { 20 | "@vue/cli-plugin-babel": "~5.0.0", 21 | "@vue/cli-plugin-router": "~5.0.0", 22 | "@vue/cli-service": "~5.0.0", 23 | "babel-plugin-component": "^1.1.1", 24 | "babel-plugin-import": "^1.13.8", 25 | "sass": "^1.66.1", 26 | "sass-loader": "^13.3.2", 27 | "svg-sprite-loader": "^6.0.11", 28 | "vue-particles": "^1.0.9", 29 | "vue-template-compiler": "^2.6.14" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /public/download.html: -------------------------------------------------------------------------------- 1 | 2 | 各平台代理工具集合 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 19 | 24 | 25 | 26 |
27 |

Clash全平台客户端备份

28 |

点击查看

29 |

代理客户端

30 |
Android TV(包括国行索尼电视、FireTv系列)
31 | 37 |
iOS 客户端 (全区可下载,包括中区)
38 | 41 |
iOS 客户端 (中区无法下载,其他区可下载)
42 | 64 |
tvOS 客户端
65 | 75 |
Android/安卓 客户端
76 | 100 |
Windows 客户端
101 | 117 |
macOS 客户端
118 | 130 |
路由器
131 | 138 |

Telegram 客户端 Proxy 代理设置

139 |

点击查看

140 |

耗电/流量

141 |

为什么使用了代理应用后在电量统计中显示耗电很多?

142 | 这是移动操作系统的一个特殊机制,Surge iOS、Quantumult X、Shadowrocket、Choc 等等所有的 SS/SSR/V2Ray/Trojan 客户端开启后会接管全局的(几乎)所有通信,所以所有的网络方面电量消耗都会被算在代理应用头上,实际使用中不会感到代理应用对电量有明显影响,「设置-电池」中看到它的电池用量,绝大部分都是网络所消耗的电量,并不是代理应用消耗的电量,代理应用就是背锅侠。

143 | 还有,夜间或锁屏后系统也会请求网络,比如后台刷新、系统服务等等,只要有请求网络就会经过代理应用,所以在「设置-电池」处看到代理应用在后台也会运行。

144 | 网络流量也是如此的。

145 | 146 |
147 | 148 | 149 |

⚠️本站部分资源引用自TG-@congcongx_bot


150 | 151 | 152 | 160 | 161 | 162 | 163 | 164 | 165 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/youshandefeiyang/sub-web-modify/68422ba384475acb791fd65eb30ada7f38634a6b/public/favicon.ico -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/youshandefeiyang/sub-web-modify/68422ba384475acb791fd65eb30ada7f38634a6b/public/favicon.png -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 在线订阅转换工具 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 |
20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /public/js/3d-lines-animation.js: -------------------------------------------------------------------------------- 1 | var mouseX = 0, mouseY = 0, 2 | 3 | windowHalfX = window.innerWidth / 2, 4 | windowHalfY = window.innerHeight / 2, 5 | 6 | SEPARATION = 200, 7 | AMOUNTX = 1, 8 | AMOUNTY = 1, 9 | 10 | camera, scene, renderer; 11 | 12 | init(); 13 | animate(); 14 | 15 | 16 | 17 | function init() { 18 | 19 | 20 | /* 21 | * Define variables 22 | */ 23 | var container, separation = 1000, amountX = 50, amountY = 50, color = 0xffffff, 24 | particles, particle; 25 | 26 | container = document.getElementById("canvas"); 27 | 28 | 29 | camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 ); 30 | camera.position.z = 100; 31 | 32 | scene = new THREE.Scene(); 33 | 34 | renderer = new THREE.CanvasRenderer({ alpha: true }); 35 | renderer.setPixelRatio( window.devicePixelRatio ); 36 | renderer.setClearColor( 0x000000, 0 ); // canvas background color 37 | renderer.setSize( window.innerWidth, window.innerHeight ); 38 | container.appendChild( renderer.domElement ); 39 | 40 | 41 | 42 | var PI2 = Math.PI * 2; 43 | var material = new THREE.SpriteCanvasMaterial( { 44 | 45 | color: color, 46 | opacity: 0.5, 47 | program: function ( context ) { 48 | 49 | context.beginPath(); 50 | context.arc( 0, 0, 0.5, 0, PI2, true ); 51 | context.fill(); 52 | 53 | } 54 | 55 | } ); 56 | 57 | var geometry = new THREE.Geometry(); 58 | 59 | /* 60 | * Number of particles 61 | */ 62 | for ( var i = 0; i < 150; i ++ ) { 63 | 64 | particle = new THREE.Sprite( material ); 65 | particle.position.x = Math.random() * 2 - 1; 66 | particle.position.y = Math.random() * 2 - 1; 67 | particle.position.z = Math.random() * 2 - 1; 68 | particle.position.normalize(); 69 | particle.position.multiplyScalar( Math.random() * 10 + 600 ); 70 | particle.scale.x = particle.scale.y = 5; 71 | 72 | scene.add( particle ); 73 | 74 | geometry.vertices.push( particle.position ); 75 | 76 | } 77 | 78 | /* 79 | * Lines 80 | */ 81 | 82 | var line = new THREE.Line( geometry, new THREE.LineBasicMaterial( { color: color, opacity: 0.2 } ) ); 83 | scene.add( line ); 84 | 85 | document.addEventListener( 'mousemove', onDocumentMouseMove, false ); 86 | document.addEventListener( 'touchstart', onDocumentTouchStart, false ); 87 | document.addEventListener( 'touchmove', onDocumentTouchMove, false ); 88 | 89 | // 90 | 91 | window.addEventListener( 'resize', onWindowResize, false ); 92 | 93 | } 94 | 95 | function onWindowResize() { 96 | 97 | windowHalfX = window.innerWidth / 2; 98 | windowHalfY = window.innerHeight / 2; 99 | 100 | camera.aspect = window.innerWidth / window.innerHeight; 101 | camera.updateProjectionMatrix(); 102 | 103 | renderer.setSize( window.innerWidth, window.innerHeight ); 104 | 105 | } 106 | 107 | // 108 | 109 | function onDocumentMouseMove(event) { 110 | 111 | mouseX = (event.clientX - windowHalfX) * 0.05; 112 | mouseY = (event.clientY - windowHalfY) * 0.2; 113 | 114 | } 115 | 116 | function onDocumentTouchStart( event ) { 117 | 118 | if ( event.touches.length > 1 ) { 119 | 120 | event.preventDefault(); 121 | 122 | mouseX = (event.touches[ 0 ].pageX - windowHalfX) * 0.7; 123 | mouseY = (event.touches[ 0 ].pageY - windowHalfY) * 0.7; 124 | 125 | } 126 | 127 | } 128 | 129 | function onDocumentTouchMove( event ) { 130 | 131 | if ( event.touches.length == 1 ) { 132 | 133 | event.preventDefault(); 134 | 135 | mouseX = event.touches[ 0 ].pageX - windowHalfX; 136 | mouseY = event.touches[ 0 ].pageY - windowHalfY; 137 | 138 | } 139 | 140 | } 141 | 142 | // 143 | 144 | function animate() { 145 | 146 | requestAnimationFrame( animate ); 147 | 148 | render(); 149 | 150 | } 151 | 152 | function render() { 153 | 154 | camera.position.x += ( mouseX - camera.position.x ) * 0.1; 155 | camera.position.y += ( - mouseY + 200 - camera.position.y ) * 0.05; 156 | camera.lookAt( scene.position ); 157 | 158 | renderer.render( scene, camera ); 159 | 160 | } -------------------------------------------------------------------------------- /public/js/canvas-renderer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | */ 4 | 5 | THREE.SpriteCanvasMaterial = function ( parameters ) { 6 | 7 | THREE.Material.call( this ); 8 | 9 | this.type = 'SpriteCanvasMaterial'; 10 | 11 | this.color = new THREE.Color( 0xffffff ); 12 | this.program = function ( context, color ) {}; 13 | 14 | this.setValues( parameters ); 15 | 16 | }; 17 | 18 | THREE.SpriteCanvasMaterial.prototype = Object.create( THREE.Material.prototype ); 19 | THREE.SpriteCanvasMaterial.prototype.constructor = THREE.SpriteCanvasMaterial; 20 | 21 | THREE.SpriteCanvasMaterial.prototype.clone = function () { 22 | 23 | var material = new THREE.SpriteCanvasMaterial(); 24 | 25 | THREE.Material.prototype.clone.call( this, material ); 26 | 27 | material.color.copy( this.color ); 28 | material.program = this.program; 29 | 30 | return material; 31 | 32 | }; 33 | 34 | // 35 | 36 | THREE.CanvasRenderer = function ( parameters ) { 37 | 38 | console.log( 'THREE.CanvasRenderer', THREE.REVISION ); 39 | 40 | var smoothstep = THREE.Math.smoothstep; 41 | 42 | parameters = parameters || {}; 43 | 44 | var _this = this, 45 | _renderData, _elements, _lights, 46 | _projector = new THREE.Projector(), 47 | 48 | _canvas = parameters.canvas !== undefined 49 | ? parameters.canvas 50 | : document.createElement( 'canvas' ), 51 | 52 | _canvasWidth = _canvas.width, 53 | _canvasHeight = _canvas.height, 54 | _canvasWidthHalf = Math.floor( _canvasWidth / 2 ), 55 | _canvasHeightHalf = Math.floor( _canvasHeight / 2 ), 56 | 57 | _viewportX = 0, 58 | _viewportY = 0, 59 | _viewportWidth = _canvasWidth, 60 | _viewportHeight = _canvasHeight, 61 | 62 | pixelRatio = 1, 63 | 64 | _context = _canvas.getContext( '2d', { 65 | alpha: parameters.alpha === true 66 | } ), 67 | 68 | _clearColor = new THREE.Color( 0x000000 ), 69 | _clearAlpha = parameters.alpha === true ? 0 : 1, 70 | 71 | _contextGlobalAlpha = 1, 72 | _contextGlobalCompositeOperation = 0, 73 | _contextStrokeStyle = null, 74 | _contextFillStyle = null, 75 | _contextLineWidth = null, 76 | _contextLineCap = null, 77 | _contextLineJoin = null, 78 | _contextLineDash = [], 79 | 80 | _camera, 81 | 82 | _v1, _v2, _v3, _v4, 83 | _v5 = new THREE.RenderableVertex(), 84 | _v6 = new THREE.RenderableVertex(), 85 | 86 | _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, 87 | _v4x, _v4y, _v5x, _v5y, _v6x, _v6y, 88 | 89 | _color = new THREE.Color(), 90 | _color1 = new THREE.Color(), 91 | _color2 = new THREE.Color(), 92 | _color3 = new THREE.Color(), 93 | _color4 = new THREE.Color(), 94 | 95 | _diffuseColor = new THREE.Color(), 96 | _emissiveColor = new THREE.Color(), 97 | 98 | _lightColor = new THREE.Color(), 99 | 100 | _patterns = {}, 101 | 102 | _image, _uvs, 103 | _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, 104 | 105 | _clipBox = new THREE.Box2(), 106 | _clearBox = new THREE.Box2(), 107 | _elemBox = new THREE.Box2(), 108 | 109 | _ambientLight = new THREE.Color(), 110 | _directionalLights = new THREE.Color(), 111 | _pointLights = new THREE.Color(), 112 | 113 | _vector3 = new THREE.Vector3(), // Needed for PointLight 114 | _centroid = new THREE.Vector3(), 115 | _normal = new THREE.Vector3(), 116 | _normalViewMatrix = new THREE.Matrix3(); 117 | 118 | // dash+gap fallbacks for Firefox and everything else 119 | 120 | if ( _context.setLineDash === undefined ) { 121 | 122 | _context.setLineDash = function () {} 123 | 124 | } 125 | 126 | this.domElement = _canvas; 127 | 128 | this.autoClear = true; 129 | this.sortObjects = true; 130 | this.sortElements = true; 131 | 132 | this.info = { 133 | 134 | render: { 135 | 136 | vertices: 0, 137 | faces: 0 138 | 139 | } 140 | 141 | } 142 | 143 | // WebGLRenderer compatibility 144 | 145 | this.supportsVertexTextures = function () {}; 146 | this.setFaceCulling = function () {}; 147 | 148 | // 149 | 150 | this.getPixelRatio = function () { 151 | 152 | return pixelRatio; 153 | 154 | }; 155 | 156 | this.setPixelRatio = function ( value ) { 157 | 158 | pixelRatio = value; 159 | 160 | }; 161 | 162 | this.setSize = function ( width, height, updateStyle ) { 163 | 164 | _canvasWidth = width * pixelRatio; 165 | _canvasHeight = height * pixelRatio; 166 | 167 | _canvas.width = _canvasWidth; 168 | _canvas.height = _canvasHeight; 169 | 170 | _canvasWidthHalf = Math.floor( _canvasWidth / 2 ); 171 | _canvasHeightHalf = Math.floor( _canvasHeight / 2 ); 172 | 173 | if ( updateStyle !== false ) { 174 | 175 | _canvas.style.width = width + 'px'; 176 | _canvas.style.height = height + 'px'; 177 | 178 | } 179 | 180 | _clipBox.min.set( -_canvasWidthHalf, -_canvasHeightHalf ), 181 | _clipBox.max.set( _canvasWidthHalf, _canvasHeightHalf ); 182 | 183 | _clearBox.min.set( - _canvasWidthHalf, - _canvasHeightHalf ); 184 | _clearBox.max.set( _canvasWidthHalf, _canvasHeightHalf ); 185 | 186 | _contextGlobalAlpha = 1; 187 | _contextGlobalCompositeOperation = 0; 188 | _contextStrokeStyle = null; 189 | _contextFillStyle = null; 190 | _contextLineWidth = null; 191 | _contextLineCap = null; 192 | _contextLineJoin = null; 193 | 194 | this.setViewport( 0, 0, width, height ); 195 | 196 | }; 197 | 198 | this.setViewport = function ( x, y, width, height ) { 199 | 200 | _viewportX = x * pixelRatio; 201 | _viewportY = y * pixelRatio; 202 | 203 | _viewportWidth = width * pixelRatio; 204 | _viewportHeight = height * pixelRatio; 205 | 206 | }; 207 | 208 | this.setScissor = function () {}; 209 | this.enableScissorTest = function () {}; 210 | 211 | this.setClearColor = function ( color, alpha ) { 212 | 213 | _clearColor.set( color ); 214 | _clearAlpha = alpha !== undefined ? alpha : 1; 215 | 216 | _clearBox.min.set( - _canvasWidthHalf, - _canvasHeightHalf ); 217 | _clearBox.max.set( _canvasWidthHalf, _canvasHeightHalf ); 218 | 219 | }; 220 | 221 | this.setClearColorHex = function ( hex, alpha ) { 222 | 223 | console.warn( 'THREE.CanvasRenderer: .setClearColorHex() is being removed. Use .setClearColor() instead.' ); 224 | this.setClearColor( hex, alpha ); 225 | 226 | }; 227 | 228 | this.getClearColor = function () { 229 | 230 | return _clearColor; 231 | 232 | }; 233 | 234 | this.getClearAlpha = function () { 235 | 236 | return _clearAlpha; 237 | 238 | }; 239 | 240 | this.getMaxAnisotropy = function () { 241 | 242 | return 0; 243 | 244 | }; 245 | 246 | this.clear = function () { 247 | 248 | if ( _clearBox.empty() === false ) { 249 | 250 | _clearBox.intersect( _clipBox ); 251 | _clearBox.expandByScalar( 2 ); 252 | 253 | _clearBox.min.x = _clearBox.min.x + _canvasWidthHalf; 254 | _clearBox.min.y = - _clearBox.min.y + _canvasHeightHalf; // higher y value ! 255 | _clearBox.max.x = _clearBox.max.x + _canvasWidthHalf; 256 | _clearBox.max.y = - _clearBox.max.y + _canvasHeightHalf; // lower y value ! 257 | 258 | if ( _clearAlpha < 1 ) { 259 | 260 | _context.clearRect( 261 | _clearBox.min.x | 0, 262 | _clearBox.max.y | 0, 263 | ( _clearBox.max.x - _clearBox.min.x ) | 0, 264 | ( _clearBox.min.y - _clearBox.max.y ) | 0 265 | ); 266 | 267 | } 268 | 269 | if ( _clearAlpha > 0 ) { 270 | 271 | setBlending( THREE.NormalBlending ); 272 | setOpacity( 1 ); 273 | 274 | setFillStyle( 'rgba(' + Math.floor( _clearColor.r * 255 ) + ',' + Math.floor( _clearColor.g * 255 ) + ',' + Math.floor( _clearColor.b * 255 ) + ',' + _clearAlpha + ')' ); 275 | 276 | _context.fillRect( 277 | _clearBox.min.x | 0, 278 | _clearBox.max.y | 0, 279 | ( _clearBox.max.x - _clearBox.min.x ) | 0, 280 | ( _clearBox.min.y - _clearBox.max.y ) | 0 281 | ); 282 | 283 | } 284 | 285 | _clearBox.makeEmpty(); 286 | 287 | } 288 | 289 | }; 290 | 291 | // compatibility 292 | 293 | this.clearColor = function () {}; 294 | this.clearDepth = function () {}; 295 | this.clearStencil = function () {}; 296 | 297 | this.render = function ( scene, camera ) { 298 | 299 | if ( camera instanceof THREE.Camera === false ) { 300 | 301 | console.error( 'THREE.CanvasRenderer.render: camera is not an instance of THREE.Camera.' ); 302 | return; 303 | 304 | } 305 | 306 | if ( this.autoClear === true ) this.clear(); 307 | 308 | _this.info.render.vertices = 0; 309 | _this.info.render.faces = 0; 310 | 311 | _context.setTransform( _viewportWidth / _canvasWidth, 0, 0, - _viewportHeight / _canvasHeight, _viewportX, _canvasHeight - _viewportY ); 312 | _context.translate( _canvasWidthHalf, _canvasHeightHalf ); 313 | 314 | _renderData = _projector.projectScene( scene, camera, this.sortObjects, this.sortElements ); 315 | _elements = _renderData.elements; 316 | _lights = _renderData.lights; 317 | _camera = camera; 318 | 319 | _normalViewMatrix.getNormalMatrix( camera.matrixWorldInverse ); 320 | 321 | /* DEBUG 322 | setFillStyle( 'rgba( 0, 255, 255, 0.5 )' ); 323 | _context.fillRect( _clipBox.min.x, _clipBox.min.y, _clipBox.max.x - _clipBox.min.x, _clipBox.max.y - _clipBox.min.y ); 324 | */ 325 | 326 | calculateLights(); 327 | 328 | for ( var e = 0, el = _elements.length; e < el; e ++ ) { 329 | 330 | var element = _elements[ e ]; 331 | 332 | var material = element.material; 333 | 334 | if ( material === undefined || material.opacity === 0 ) continue; 335 | 336 | _elemBox.makeEmpty(); 337 | 338 | if ( element instanceof THREE.RenderableSprite ) { 339 | 340 | _v1 = element; 341 | _v1.x *= _canvasWidthHalf; _v1.y *= _canvasHeightHalf; 342 | 343 | renderSprite( _v1, element, material ); 344 | 345 | } else if ( element instanceof THREE.RenderableLine ) { 346 | 347 | _v1 = element.v1; _v2 = element.v2; 348 | 349 | _v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf; 350 | _v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf; 351 | 352 | _elemBox.setFromPoints( [ 353 | _v1.positionScreen, 354 | _v2.positionScreen 355 | ] ); 356 | 357 | if ( _clipBox.isIntersectionBox( _elemBox ) === true ) { 358 | 359 | renderLine( _v1, _v2, element, material ); 360 | 361 | } 362 | 363 | } else if ( element instanceof THREE.RenderableFace ) { 364 | 365 | _v1 = element.v1; _v2 = element.v2; _v3 = element.v3; 366 | 367 | if ( _v1.positionScreen.z < - 1 || _v1.positionScreen.z > 1 ) continue; 368 | if ( _v2.positionScreen.z < - 1 || _v2.positionScreen.z > 1 ) continue; 369 | if ( _v3.positionScreen.z < - 1 || _v3.positionScreen.z > 1 ) continue; 370 | 371 | _v1.positionScreen.x *= _canvasWidthHalf; _v1.positionScreen.y *= _canvasHeightHalf; 372 | _v2.positionScreen.x *= _canvasWidthHalf; _v2.positionScreen.y *= _canvasHeightHalf; 373 | _v3.positionScreen.x *= _canvasWidthHalf; _v3.positionScreen.y *= _canvasHeightHalf; 374 | 375 | if ( material.overdraw > 0 ) { 376 | 377 | expand( _v1.positionScreen, _v2.positionScreen, material.overdraw ); 378 | expand( _v2.positionScreen, _v3.positionScreen, material.overdraw ); 379 | expand( _v3.positionScreen, _v1.positionScreen, material.overdraw ); 380 | 381 | } 382 | 383 | _elemBox.setFromPoints( [ 384 | _v1.positionScreen, 385 | _v2.positionScreen, 386 | _v3.positionScreen 387 | ] ); 388 | 389 | if ( _clipBox.isIntersectionBox( _elemBox ) === true ) { 390 | 391 | renderFace3( _v1, _v2, _v3, 0, 1, 2, element, material ); 392 | 393 | } 394 | 395 | } 396 | 397 | /* DEBUG 398 | setLineWidth( 1 ); 399 | setStrokeStyle( 'rgba( 0, 255, 0, 0.5 )' ); 400 | _context.strokeRect( _elemBox.min.x, _elemBox.min.y, _elemBox.max.x - _elemBox.min.x, _elemBox.max.y - _elemBox.min.y ); 401 | */ 402 | 403 | _clearBox.union( _elemBox ); 404 | 405 | } 406 | 407 | /* DEBUG 408 | setLineWidth( 1 ); 409 | setStrokeStyle( 'rgba( 255, 0, 0, 0.5 )' ); 410 | _context.strokeRect( _clearBox.min.x, _clearBox.min.y, _clearBox.max.x - _clearBox.min.x, _clearBox.max.y - _clearBox.min.y ); 411 | */ 412 | 413 | _context.setTransform( 1, 0, 0, 1, 0, 0 ); 414 | 415 | }; 416 | 417 | // 418 | 419 | function calculateLights() { 420 | 421 | _ambientLight.setRGB( 0, 0, 0 ); 422 | _directionalLights.setRGB( 0, 0, 0 ); 423 | _pointLights.setRGB( 0, 0, 0 ); 424 | 425 | for ( var l = 0, ll = _lights.length; l < ll; l ++ ) { 426 | 427 | var light = _lights[ l ]; 428 | var lightColor = light.color; 429 | 430 | if ( light instanceof THREE.AmbientLight ) { 431 | 432 | _ambientLight.add( lightColor ); 433 | 434 | } else if ( light instanceof THREE.DirectionalLight ) { 435 | 436 | // for sprites 437 | 438 | _directionalLights.add( lightColor ); 439 | 440 | } else if ( light instanceof THREE.PointLight ) { 441 | 442 | // for sprites 443 | 444 | _pointLights.add( lightColor ); 445 | 446 | } 447 | 448 | } 449 | 450 | } 451 | 452 | function calculateLight( position, normal, color ) { 453 | 454 | for ( var l = 0, ll = _lights.length; l < ll; l ++ ) { 455 | 456 | var light = _lights[ l ]; 457 | 458 | _lightColor.copy( light.color ); 459 | 460 | if ( light instanceof THREE.DirectionalLight ) { 461 | 462 | var lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld ).normalize(); 463 | 464 | var amount = normal.dot( lightPosition ); 465 | 466 | if ( amount <= 0 ) continue; 467 | 468 | amount *= light.intensity; 469 | 470 | color.add( _lightColor.multiplyScalar( amount ) ); 471 | 472 | } else if ( light instanceof THREE.PointLight ) { 473 | 474 | var lightPosition = _vector3.setFromMatrixPosition( light.matrixWorld ); 475 | 476 | var amount = normal.dot( _vector3.subVectors( lightPosition, position ).normalize() ); 477 | 478 | if ( amount <= 0 ) continue; 479 | 480 | amount *= light.distance == 0 ? 1 : 1 - Math.min( position.distanceTo( lightPosition ) / light.distance, 1 ); 481 | 482 | if ( amount == 0 ) continue; 483 | 484 | amount *= light.intensity; 485 | 486 | color.add( _lightColor.multiplyScalar( amount ) ); 487 | 488 | } 489 | 490 | } 491 | 492 | } 493 | 494 | function renderSprite( v1, element, material ) { 495 | 496 | setOpacity( material.opacity ); 497 | setBlending( material.blending ); 498 | 499 | var scaleX = element.scale.x * _canvasWidthHalf; 500 | var scaleY = element.scale.y * _canvasHeightHalf; 501 | 502 | var dist = 0.5 * Math.sqrt( scaleX * scaleX + scaleY * scaleY ); // allow for rotated sprite 503 | _elemBox.min.set( v1.x - dist, v1.y - dist ); 504 | _elemBox.max.set( v1.x + dist, v1.y + dist ); 505 | 506 | if ( material instanceof THREE.SpriteMaterial ) { 507 | 508 | var texture = material.map; 509 | 510 | if ( texture !== null && texture.image !== undefined ) { 511 | 512 | if ( texture.hasEventListener( 'update', onTextureUpdate ) === false ) { 513 | 514 | if ( texture.image.width > 0 ) { 515 | 516 | textureToPattern( texture ); 517 | 518 | } 519 | 520 | texture.addEventListener( 'update', onTextureUpdate ); 521 | 522 | } 523 | 524 | var pattern = _patterns[ texture.id ]; 525 | 526 | if ( pattern !== undefined ) { 527 | 528 | setFillStyle( pattern ); 529 | 530 | } else { 531 | 532 | setFillStyle( 'rgba( 0, 0, 0, 1 )' ); 533 | 534 | } 535 | 536 | // 537 | 538 | var bitmap = texture.image; 539 | 540 | var ox = bitmap.width * texture.offset.x; 541 | var oy = bitmap.height * texture.offset.y; 542 | 543 | var sx = bitmap.width * texture.repeat.x; 544 | var sy = bitmap.height * texture.repeat.y; 545 | 546 | var cx = scaleX / sx; 547 | var cy = scaleY / sy; 548 | 549 | _context.save(); 550 | _context.translate( v1.x, v1.y ); 551 | if ( material.rotation !== 0 ) _context.rotate( material.rotation ); 552 | _context.translate( - scaleX / 2, - scaleY / 2 ); 553 | _context.scale( cx, cy ); 554 | _context.translate( - ox, - oy ); 555 | _context.fillRect( ox, oy, sx, sy ); 556 | _context.restore(); 557 | 558 | } else { 559 | 560 | // no texture 561 | 562 | setFillStyle( material.color.getStyle() ); 563 | 564 | _context.save(); 565 | _context.translate( v1.x, v1.y ); 566 | if ( material.rotation !== 0 ) _context.rotate( material.rotation ); 567 | _context.scale( scaleX, - scaleY ); 568 | _context.fillRect( - 0.5, - 0.5, 1, 1 ); 569 | _context.restore(); 570 | 571 | } 572 | 573 | } else if ( material instanceof THREE.SpriteCanvasMaterial ) { 574 | 575 | setStrokeStyle( material.color.getStyle() ); 576 | setFillStyle( material.color.getStyle() ); 577 | 578 | _context.save(); 579 | _context.translate( v1.x, v1.y ); 580 | if ( material.rotation !== 0 ) _context.rotate( material.rotation ); 581 | _context.scale( scaleX, scaleY ); 582 | 583 | material.program( _context ); 584 | 585 | _context.restore(); 586 | 587 | } 588 | 589 | /* DEBUG 590 | setStrokeStyle( 'rgb(255,255,0)' ); 591 | _context.beginPath(); 592 | _context.moveTo( v1.x - 10, v1.y ); 593 | _context.lineTo( v1.x + 10, v1.y ); 594 | _context.moveTo( v1.x, v1.y - 10 ); 595 | _context.lineTo( v1.x, v1.y + 10 ); 596 | _context.stroke(); 597 | */ 598 | 599 | } 600 | 601 | function renderLine( v1, v2, element, material ) { 602 | 603 | setOpacity( material.opacity ); 604 | setBlending( material.blending ); 605 | 606 | _context.beginPath(); 607 | _context.moveTo( v1.positionScreen.x, v1.positionScreen.y ); 608 | _context.lineTo( v2.positionScreen.x, v2.positionScreen.y ); 609 | 610 | if ( material instanceof THREE.LineBasicMaterial ) { 611 | 612 | setLineWidth( material.linewidth ); 613 | setLineCap( material.linecap ); 614 | setLineJoin( material.linejoin ); 615 | 616 | if ( material.vertexColors !== THREE.VertexColors ) { 617 | 618 | setStrokeStyle( material.color.getStyle() ); 619 | 620 | } else { 621 | 622 | var colorStyle1 = element.vertexColors[ 0 ].getStyle(); 623 | var colorStyle2 = element.vertexColors[ 1 ].getStyle(); 624 | 625 | if ( colorStyle1 === colorStyle2 ) { 626 | 627 | setStrokeStyle( colorStyle1 ); 628 | 629 | } else { 630 | 631 | try { 632 | 633 | var grad = _context.createLinearGradient( 634 | v1.positionScreen.x, 635 | v1.positionScreen.y, 636 | v2.positionScreen.x, 637 | v2.positionScreen.y 638 | ); 639 | grad.addColorStop( 0, colorStyle1 ); 640 | grad.addColorStop( 1, colorStyle2 ); 641 | 642 | } catch ( exception ) { 643 | 644 | grad = colorStyle1; 645 | 646 | } 647 | 648 | setStrokeStyle( grad ); 649 | 650 | } 651 | 652 | } 653 | 654 | _context.stroke(); 655 | _elemBox.expandByScalar( material.linewidth * 2 ); 656 | 657 | } else if ( material instanceof THREE.LineDashedMaterial ) { 658 | 659 | setLineWidth( material.linewidth ); 660 | setLineCap( material.linecap ); 661 | setLineJoin( material.linejoin ); 662 | setStrokeStyle( material.color.getStyle() ); 663 | setLineDash( [ material.dashSize, material.gapSize ] ); 664 | 665 | _context.stroke(); 666 | 667 | _elemBox.expandByScalar( material.linewidth * 2 ); 668 | 669 | setLineDash( [] ); 670 | 671 | } 672 | 673 | } 674 | 675 | function renderFace3( v1, v2, v3, uv1, uv2, uv3, element, material ) { 676 | 677 | _this.info.render.vertices += 3; 678 | _this.info.render.faces ++; 679 | 680 | setOpacity( material.opacity ); 681 | setBlending( material.blending ); 682 | 683 | _v1x = v1.positionScreen.x; _v1y = v1.positionScreen.y; 684 | _v2x = v2.positionScreen.x; _v2y = v2.positionScreen.y; 685 | _v3x = v3.positionScreen.x; _v3y = v3.positionScreen.y; 686 | 687 | drawTriangle( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y ); 688 | 689 | if ( ( material instanceof THREE.MeshLambertMaterial || material instanceof THREE.MeshPhongMaterial ) && material.map === null ) { 690 | 691 | _diffuseColor.copy( material.color ); 692 | _emissiveColor.copy( material.emissive ); 693 | 694 | if ( material.vertexColors === THREE.FaceColors ) { 695 | 696 | _diffuseColor.multiply( element.color ); 697 | 698 | } 699 | 700 | _color.copy( _ambientLight ); 701 | 702 | _centroid.copy( v1.positionWorld ).add( v2.positionWorld ).add( v3.positionWorld ).divideScalar( 3 ); 703 | 704 | calculateLight( _centroid, element.normalModel, _color ); 705 | 706 | _color.multiply( _diffuseColor ).add( _emissiveColor ); 707 | 708 | material.wireframe === true 709 | ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) 710 | : fillPath( _color ); 711 | 712 | } else if ( material instanceof THREE.MeshBasicMaterial || 713 | material instanceof THREE.MeshLambertMaterial || 714 | material instanceof THREE.MeshPhongMaterial ) { 715 | 716 | if ( material.map !== null ) { 717 | 718 | var mapping = material.map.mapping; 719 | 720 | if ( mapping === THREE.UVMapping ) { 721 | 722 | _uvs = element.uvs; 723 | patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uvs[ uv1 ].x, _uvs[ uv1 ].y, _uvs[ uv2 ].x, _uvs[ uv2 ].y, _uvs[ uv3 ].x, _uvs[ uv3 ].y, material.map ); 724 | 725 | } 726 | 727 | } else if ( material.envMap !== null ) { 728 | 729 | if ( material.envMap.mapping === THREE.SphericalReflectionMapping ) { 730 | 731 | _normal.copy( element.vertexNormalsModel[ uv1 ] ).applyMatrix3( _normalViewMatrix ); 732 | _uv1x = 0.5 * _normal.x + 0.5; 733 | _uv1y = 0.5 * _normal.y + 0.5; 734 | 735 | _normal.copy( element.vertexNormalsModel[ uv2 ] ).applyMatrix3( _normalViewMatrix ); 736 | _uv2x = 0.5 * _normal.x + 0.5; 737 | _uv2y = 0.5 * _normal.y + 0.5; 738 | 739 | _normal.copy( element.vertexNormalsModel[ uv3 ] ).applyMatrix3( _normalViewMatrix ); 740 | _uv3x = 0.5 * _normal.x + 0.5; 741 | _uv3y = 0.5 * _normal.y + 0.5; 742 | 743 | patternPath( _v1x, _v1y, _v2x, _v2y, _v3x, _v3y, _uv1x, _uv1y, _uv2x, _uv2y, _uv3x, _uv3y, material.envMap ); 744 | 745 | } 746 | 747 | } else { 748 | 749 | _color.copy( material.color ); 750 | 751 | if ( material.vertexColors === THREE.FaceColors ) { 752 | 753 | _color.multiply( element.color ); 754 | 755 | } 756 | 757 | material.wireframe === true 758 | ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) 759 | : fillPath( _color ); 760 | 761 | } 762 | 763 | } else if ( material instanceof THREE.MeshDepthMaterial ) { 764 | 765 | _color.r = _color.g = _color.b = 1 - smoothstep( v1.positionScreen.z * v1.positionScreen.w, _camera.near, _camera.far ); 766 | 767 | material.wireframe === true 768 | ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) 769 | : fillPath( _color ); 770 | 771 | } else if ( material instanceof THREE.MeshNormalMaterial ) { 772 | 773 | _normal.copy( element.normalModel ).applyMatrix3( _normalViewMatrix ); 774 | 775 | _color.setRGB( _normal.x, _normal.y, _normal.z ).multiplyScalar( 0.5 ).addScalar( 0.5 ); 776 | 777 | material.wireframe === true 778 | ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) 779 | : fillPath( _color ); 780 | 781 | } else { 782 | 783 | _color.setRGB( 1, 1, 1 ); 784 | 785 | material.wireframe === true 786 | ? strokePath( _color, material.wireframeLinewidth, material.wireframeLinecap, material.wireframeLinejoin ) 787 | : fillPath( _color ); 788 | 789 | } 790 | 791 | } 792 | 793 | // 794 | 795 | function drawTriangle( x0, y0, x1, y1, x2, y2 ) { 796 | 797 | _context.beginPath(); 798 | _context.moveTo( x0, y0 ); 799 | _context.lineTo( x1, y1 ); 800 | _context.lineTo( x2, y2 ); 801 | _context.closePath(); 802 | 803 | } 804 | 805 | function strokePath( color, linewidth, linecap, linejoin ) { 806 | 807 | setLineWidth( linewidth ); 808 | setLineCap( linecap ); 809 | setLineJoin( linejoin ); 810 | setStrokeStyle( color.getStyle() ); 811 | 812 | _context.stroke(); 813 | 814 | _elemBox.expandByScalar( linewidth * 2 ); 815 | 816 | } 817 | 818 | function fillPath( color ) { 819 | 820 | setFillStyle( color.getStyle() ); 821 | _context.fill(); 822 | 823 | } 824 | 825 | function onTextureUpdate ( event ) { 826 | 827 | textureToPattern( event.target ); 828 | 829 | } 830 | 831 | function textureToPattern( texture ) { 832 | 833 | if ( texture instanceof THREE.CompressedTexture ) return; 834 | 835 | var repeatX = texture.wrapS === THREE.RepeatWrapping; 836 | var repeatY = texture.wrapT === THREE.RepeatWrapping; 837 | 838 | var image = texture.image; 839 | 840 | var canvas = document.createElement( 'canvas' ); 841 | canvas.width = image.width; 842 | canvas.height = image.height; 843 | 844 | var context = canvas.getContext( '2d' ); 845 | context.setTransform( 1, 0, 0, - 1, 0, image.height ); 846 | context.drawImage( image, 0, 0 ); 847 | 848 | _patterns[ texture.id ] = _context.createPattern( 849 | canvas, repeatX === true && repeatY === true 850 | ? 'repeat' 851 | : repeatX === true && repeatY === false 852 | ? 'repeat-x' 853 | : repeatX === false && repeatY === true 854 | ? 'repeat-y' 855 | : 'no-repeat' 856 | ); 857 | 858 | } 859 | 860 | function patternPath( x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2, texture ) { 861 | 862 | if ( texture instanceof THREE.DataTexture ) return; 863 | 864 | if ( texture.hasEventListener( 'update', onTextureUpdate ) === false ) { 865 | 866 | if ( texture.image !== undefined && texture.image.width > 0 ) { 867 | 868 | textureToPattern( texture ); 869 | 870 | } 871 | 872 | texture.addEventListener( 'update', onTextureUpdate ); 873 | 874 | } 875 | 876 | var pattern = _patterns[ texture.id ]; 877 | 878 | if ( pattern !== undefined ) { 879 | 880 | setFillStyle( pattern ); 881 | 882 | } else { 883 | 884 | setFillStyle( 'rgba(0,0,0,1)' ); 885 | _context.fill(); 886 | 887 | return; 888 | 889 | } 890 | 891 | // http://extremelysatisfactorytotalitarianism.com/blog/?p=2120 892 | 893 | var a, b, c, d, e, f, det, idet, 894 | offsetX = texture.offset.x / texture.repeat.x, 895 | offsetY = texture.offset.y / texture.repeat.y, 896 | width = texture.image.width * texture.repeat.x, 897 | height = texture.image.height * texture.repeat.y; 898 | 899 | u0 = ( u0 + offsetX ) * width; 900 | v0 = ( v0 + offsetY ) * height; 901 | 902 | u1 = ( u1 + offsetX ) * width; 903 | v1 = ( v1 + offsetY ) * height; 904 | 905 | u2 = ( u2 + offsetX ) * width; 906 | v2 = ( v2 + offsetY ) * height; 907 | 908 | x1 -= x0; y1 -= y0; 909 | x2 -= x0; y2 -= y0; 910 | 911 | u1 -= u0; v1 -= v0; 912 | u2 -= u0; v2 -= v0; 913 | 914 | det = u1 * v2 - u2 * v1; 915 | 916 | if ( det === 0 ) return; 917 | 918 | idet = 1 / det; 919 | 920 | a = ( v2 * x1 - v1 * x2 ) * idet; 921 | b = ( v2 * y1 - v1 * y2 ) * idet; 922 | c = ( u1 * x2 - u2 * x1 ) * idet; 923 | d = ( u1 * y2 - u2 * y1 ) * idet; 924 | 925 | e = x0 - a * u0 - c * v0; 926 | f = y0 - b * u0 - d * v0; 927 | 928 | _context.save(); 929 | _context.transform( a, b, c, d, e, f ); 930 | _context.fill(); 931 | _context.restore(); 932 | 933 | } 934 | 935 | function clipImage( x0, y0, x1, y1, x2, y2, u0, v0, u1, v1, u2, v2, image ) { 936 | 937 | // http://extremelysatisfactorytotalitarianism.com/blog/?p=2120 938 | 939 | var a, b, c, d, e, f, det, idet, 940 | width = image.width - 1, 941 | height = image.height - 1; 942 | 943 | u0 *= width; v0 *= height; 944 | u1 *= width; v1 *= height; 945 | u2 *= width; v2 *= height; 946 | 947 | x1 -= x0; y1 -= y0; 948 | x2 -= x0; y2 -= y0; 949 | 950 | u1 -= u0; v1 -= v0; 951 | u2 -= u0; v2 -= v0; 952 | 953 | det = u1 * v2 - u2 * v1; 954 | 955 | idet = 1 / det; 956 | 957 | a = ( v2 * x1 - v1 * x2 ) * idet; 958 | b = ( v2 * y1 - v1 * y2 ) * idet; 959 | c = ( u1 * x2 - u2 * x1 ) * idet; 960 | d = ( u1 * y2 - u2 * y1 ) * idet; 961 | 962 | e = x0 - a * u0 - c * v0; 963 | f = y0 - b * u0 - d * v0; 964 | 965 | _context.save(); 966 | _context.transform( a, b, c, d, e, f ); 967 | _context.clip(); 968 | _context.drawImage( image, 0, 0 ); 969 | _context.restore(); 970 | 971 | } 972 | 973 | // Hide anti-alias gaps 974 | 975 | function expand( v1, v2, pixels ) { 976 | 977 | var x = v2.x - v1.x, y = v2.y - v1.y, 978 | det = x * x + y * y, idet; 979 | 980 | if ( det === 0 ) return; 981 | 982 | idet = pixels / Math.sqrt( det ); 983 | 984 | x *= idet; y *= idet; 985 | 986 | v2.x += x; v2.y += y; 987 | v1.x -= x; v1.y -= y; 988 | 989 | } 990 | 991 | // Context cached methods. 992 | 993 | function setOpacity( value ) { 994 | 995 | if ( _contextGlobalAlpha !== value ) { 996 | 997 | _context.globalAlpha = value; 998 | _contextGlobalAlpha = value; 999 | 1000 | } 1001 | 1002 | } 1003 | 1004 | function setBlending( value ) { 1005 | 1006 | if ( _contextGlobalCompositeOperation !== value ) { 1007 | 1008 | if ( value === THREE.NormalBlending ) { 1009 | 1010 | _context.globalCompositeOperation = 'source-over'; 1011 | 1012 | } else if ( value === THREE.AdditiveBlending ) { 1013 | 1014 | _context.globalCompositeOperation = 'lighter'; 1015 | 1016 | } else if ( value === THREE.SubtractiveBlending ) { 1017 | 1018 | _context.globalCompositeOperation = 'darker'; 1019 | 1020 | } 1021 | 1022 | _contextGlobalCompositeOperation = value; 1023 | 1024 | } 1025 | 1026 | } 1027 | 1028 | function setLineWidth( value ) { 1029 | 1030 | if ( _contextLineWidth !== value ) { 1031 | 1032 | _context.lineWidth = value; 1033 | _contextLineWidth = value; 1034 | 1035 | } 1036 | 1037 | } 1038 | 1039 | function setLineCap( value ) { 1040 | 1041 | // "butt", "round", "square" 1042 | 1043 | if ( _contextLineCap !== value ) { 1044 | 1045 | _context.lineCap = value; 1046 | _contextLineCap = value; 1047 | 1048 | } 1049 | 1050 | } 1051 | 1052 | function setLineJoin( value ) { 1053 | 1054 | // "round", "bevel", "miter" 1055 | 1056 | if ( _contextLineJoin !== value ) { 1057 | 1058 | _context.lineJoin = value; 1059 | _contextLineJoin = value; 1060 | 1061 | } 1062 | 1063 | } 1064 | 1065 | function setStrokeStyle( value ) { 1066 | 1067 | if ( _contextStrokeStyle !== value ) { 1068 | 1069 | _context.strokeStyle = value; 1070 | _contextStrokeStyle = value; 1071 | 1072 | } 1073 | 1074 | } 1075 | 1076 | function setFillStyle( value ) { 1077 | 1078 | if ( _contextFillStyle !== value ) { 1079 | 1080 | _context.fillStyle = value; 1081 | _contextFillStyle = value; 1082 | 1083 | } 1084 | 1085 | } 1086 | 1087 | function setLineDash( value ) { 1088 | 1089 | if ( _contextLineDash.length !== value.length ) { 1090 | 1091 | _context.setLineDash( value ); 1092 | _contextLineDash = value; 1093 | 1094 | } 1095 | 1096 | } 1097 | 1098 | }; 1099 | -------------------------------------------------------------------------------- /public/js/color.js: -------------------------------------------------------------------------------- 1 | 2 | var colors = new Array( 3 | [62,35,255], 4 | [60,255,60], 5 | [255,35,98], 6 | [45,175,230], 7 | [255,0,255], 8 | [255,128,0]); 9 | 10 | 11 | // 12 | 13 | var step = 0; 14 | //color table indices for: 15 | // current color left 16 | // next color left 17 | // current color right 18 | // next color right 19 | var colorIndices = [0,1,2,3]; 20 | 21 | //transition speed 22 | var gradientSpeed = 0.002; 23 | 24 | function updateGradient() 25 | { 26 | 27 | if ( $===undefined ) return; 28 | 29 | var c0_0 = colors[colorIndices[0]]; 30 | var c0_1 = colors[colorIndices[1]]; 31 | var c1_0 = colors[colorIndices[2]]; 32 | var c1_1 = colors[colorIndices[3]]; 33 | 34 | var istep = 1 - step; 35 | var r1 = Math.round(istep * c0_0[0] + step * c0_1[0]); 36 | var g1 = Math.round(istep * c0_0[1] + step * c0_1[1]); 37 | var b1 = Math.round(istep * c0_0[2] + step * c0_1[2]); 38 | var color1 = "rgb("+r1+","+g1+","+b1+")"; 39 | 40 | var r2 = Math.round(istep * c1_0[0] + step * c1_1[0]); 41 | var g2 = Math.round(istep * c1_0[1] + step * c1_1[1]); 42 | var b2 = Math.round(istep * c1_0[2] + step * c1_1[2]); 43 | var color2 = "rgb("+r2+","+g2+","+b2+")"; 44 | 45 | $('.gradient').css({ 46 | background: "-webkit-gradient(linear, left top, right top, from("+color1+"), to("+color2+"))"}).css({ 47 | background: "-moz-linear-gradient(left, "+color1+" 0%, "+color2+" 100%)"}); 48 | 49 | step += gradientSpeed; 50 | if ( step >= 1 ) 51 | { 52 | step %= 1; 53 | colorIndices[0] = colorIndices[1]; 54 | colorIndices[2] = colorIndices[3]; 55 | 56 | //pick two new target color indices 57 | //do not pick the same as the current one 58 | colorIndices[1] = ( colorIndices[1] + Math.floor( 1 + Math.random() * (colors.length - 1))) % colors.length; 59 | colorIndices[3] = ( colorIndices[3] + Math.floor( 1 + Math.random() * (colors.length - 1))) % colors.length; 60 | 61 | } 62 | } 63 | 64 | setInterval(updateGradient,10); -------------------------------------------------------------------------------- /public/js/djtx.min.js: -------------------------------------------------------------------------------- 1 | function normalize(t,s,e){return(t-s)/(e-s)}function denormalize(t,s,e){return t*(e-s)+s}function getRandomFloat(t,s){return Math.random()*(s-t)+t}function getRandomInt(t,s){return Math.floor(Math.random()*(s-t+1)+t)}class CursorSparkler{static modes={follow:"follow",trail:"trail"};static TranslateZero="translate3d(0, 0, 0)";constructor(t){(t=t||{}).mode=t.mode||CursorSparkler.modes.trail,t.numSparkles=t.numSparkles||20,t.sparkleFactor=1,t.sparkleDurationRange=[50,500],t.sparkleDistanceRange=[40,100],t.sparkleSizeRange=[1,5],this.options=t,this.el=document.createElement("div"),this.el.style.position="absolute",this.el.style.top=-this.options.sparkleSizeRange[1]+"px",this.el.style.left=-this.options.sparkleSizeRange[1]+"px",this.el.style.zIndex="10000",this.el.style.pointerEvents="none",this.el.style.width="1px",this.el.style.height="1px",this.shouldAnimate=!0,this.sparkles=[],this.onMouseMove=this.onMouseMove.bind(this),this.onMouseDown=this.onMouseDown.bind(this),this.onMouseUp=this.onMouseUp.bind(this),this.onAnimationFrame=this.onAnimationFrame.bind(this)}listen(){window.addEventListener("mousemove",this.onMouseMove),window.addEventListener("mousedown",this.onMouseDown),window.addEventListener("mouseup",this.onMouseUp),document.body.appendChild(this.el),requestAnimationFrame(this.onAnimationFrame)}destroy(){this.el.parentElement.removeChild(this.el),window.removeEventListener("mousemove",this.onMouseMove),this.shouldAnimate=!1}render(t){if(this.options.disabled)"none"!==this.el.style.display&&(this.el.style.display="none");else{"block"!==this.el.style.display&&(this.el.style.display="block"),this.options.mode===CursorSparkler.modes.follow?this.el.style.transform="translate3d("+this.x+"px, "+this.y+"px, 0)":this.el.style.transform!==CursorSparkler.TranslateZero&&(this.el.style.transform=CursorSparkler.TranslateZero);var s=this.options.numSparkles;this.sparkles.length>s&&(this.sparkles.slice(s).forEach((function(t){t.destroy()})),this.sparkles.length=s);for(var e,i=0;i=e.options.startTime+e.options.duration&&(e.destroy(),this.sparkles[i]=this.sparkle(t+100),this.el.appendChild(this.sparkles[i].el))):(this.sparkles[i]=this.sparkle(t+getRandomInt(0,800/this.options.sparkleFactor)),this.el.appendChild(this.sparkles[i].el))}}sparkle(t){var s=this.options,e=s.sparkleFactor,i=s.sparkleDurationRange,o=s.sparkleDistanceRange,r=s.sparkleSizeRange,n=s.mode===CursorSparkler.modes.trail?this.x:0,a=s.mode===CursorSparkler.modes.trail?this.y:0;return n+=s.sparkleSizeRange[1],a+=s.sparkleSizeRange[1],new Sparkle({window:this.window,startTime:t,startX:n,startY:a,duration:getRandomInt(i[0],i[1]/e),distance:getRandomInt(o[0],o[1]*(1===e?1:e/4)),size:getRandomInt(r[0],r[1]*(1===e?1:e/3))})}onMouseMove(t){this.x=t.pageX,this.y=t.pageY}onMouseDown(t){this.originalSparkleFactor?this.options.sparkleFactor=this.originalSparkleFactor:this.originalSparkleFactor=this.options.sparkleFactor,this.options.sparkleFactor*=4}onMouseUp(t){this.options.sparkleFactor=this.originalSparkleFactor||1,delete this.originalSparkleFactor}onAnimationFrame(t){this.shouldAnimate&&(this.start||(this.start=t),this.render(t),requestAnimationFrame(this.onAnimationFrame))}}class Sparkle{static fantasticColors=["yellow","pink","red","orange","purple","cyan"];constructor(t){(t=t||{}).duration=t.duration||getRandomInt(50,500),t.direction=t.direction||getRandomFloat(0,2*Math.PI),t.distance=t.distance||getRandomInt(40,100),t.size=t.size||getRandomInt(1,5),t.color=t.color||Sparkle.getFantasticColor(),t.startTime=t.startTime||0,this.options=t,this.el=document.createElement("div"),this.el.style.position="absolute",this.el.style.background=this.options.color,this.el.style.width=this.options.size+"px",this.el.style.height=this.options.size+"px",this.el.style.borderRadius="50%",this.el.style.transform="translate3d(0,0,0)"}static getFantasticColor(){return Sparkle.fantasticColors[~~(Sparkle.fantasticColors.length*Math.random())]}destroy(){this.el.parentElement.removeChild(this.el)}render(t){var s=normalize(t,this.options.startTime,this.options.startTime+this.options.duration),e=this.options.startX+Math.sin(this.options.direction)*this.options.distance*s,i=this.options.startY+Math.cos(this.options.direction)*this.options.distance*s;this.el.style.opacity=(1-s).toString(),this.el.style.transform="translate3d("+e+"px, "+i+"px, 0)"}}var sparkler=new CursorSparkler({mode:"trail"});function bootstrap(){sparkler.listen()}bootstrap(); -------------------------------------------------------------------------------- /public/js/projector.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | * @author supereggbert / http://www.paulbrunt.co.uk/ 4 | * @author julianwa / https://github.com/julianwa 5 | */ 6 | 7 | THREE.RenderableObject = function () { 8 | 9 | this.id = 0; 10 | 11 | this.object = null; 12 | this.z = 0; 13 | 14 | }; 15 | 16 | // 17 | 18 | THREE.RenderableFace = function () { 19 | 20 | this.id = 0; 21 | 22 | this.v1 = new THREE.RenderableVertex(); 23 | this.v2 = new THREE.RenderableVertex(); 24 | this.v3 = new THREE.RenderableVertex(); 25 | 26 | this.normalModel = new THREE.Vector3(); 27 | 28 | this.vertexNormalsModel = [ new THREE.Vector3(), new THREE.Vector3(), new THREE.Vector3() ]; 29 | this.vertexNormalsLength = 0; 30 | 31 | this.color = new THREE.Color(); 32 | this.material = null; 33 | this.uvs = [ new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2() ]; 34 | 35 | this.z = 0; 36 | 37 | }; 38 | 39 | // 40 | 41 | THREE.RenderableVertex = function () { 42 | 43 | this.position = new THREE.Vector3(); 44 | this.positionWorld = new THREE.Vector3(); 45 | this.positionScreen = new THREE.Vector4(); 46 | 47 | this.visible = true; 48 | 49 | }; 50 | 51 | THREE.RenderableVertex.prototype.copy = function ( vertex ) { 52 | 53 | this.positionWorld.copy( vertex.positionWorld ); 54 | this.positionScreen.copy( vertex.positionScreen ); 55 | 56 | }; 57 | 58 | // 59 | 60 | THREE.RenderableLine = function () { 61 | 62 | this.id = 0; 63 | 64 | this.v1 = new THREE.RenderableVertex(); 65 | this.v2 = new THREE.RenderableVertex(); 66 | 67 | this.vertexColors = [ new THREE.Color(), new THREE.Color() ]; 68 | this.material = null; 69 | 70 | this.z = 0; 71 | 72 | }; 73 | 74 | // 75 | 76 | THREE.RenderableSprite = function () { 77 | 78 | this.id = 0; 79 | 80 | this.object = null; 81 | 82 | this.x = 0; 83 | this.y = 0; 84 | this.z = 0; 85 | 86 | this.rotation = 0; 87 | this.scale = new THREE.Vector2(); 88 | 89 | this.material = null; 90 | 91 | }; 92 | 93 | // 94 | 95 | THREE.Projector = function () { 96 | 97 | var _object, _objectCount, _objectPool = [], _objectPoolLength = 0, 98 | _vertex, _vertexCount, _vertexPool = [], _vertexPoolLength = 0, 99 | _face, _faceCount, _facePool = [], _facePoolLength = 0, 100 | _line, _lineCount, _linePool = [], _linePoolLength = 0, 101 | _sprite, _spriteCount, _spritePool = [], _spritePoolLength = 0, 102 | 103 | _renderData = { objects: [], lights: [], elements: [] }, 104 | 105 | _vector3 = new THREE.Vector3(), 106 | _vector4 = new THREE.Vector4(), 107 | 108 | _clipBox = new THREE.Box3( new THREE.Vector3( - 1, - 1, - 1 ), new THREE.Vector3( 1, 1, 1 ) ), 109 | _boundingBox = new THREE.Box3(), 110 | _points3 = new Array( 3 ), 111 | _points4 = new Array( 4 ), 112 | 113 | _viewMatrix = new THREE.Matrix4(), 114 | _viewProjectionMatrix = new THREE.Matrix4(), 115 | 116 | _modelMatrix, 117 | _modelViewProjectionMatrix = new THREE.Matrix4(), 118 | 119 | _normalMatrix = new THREE.Matrix3(), 120 | 121 | _frustum = new THREE.Frustum(), 122 | 123 | _clippedVertex1PositionScreen = new THREE.Vector4(), 124 | _clippedVertex2PositionScreen = new THREE.Vector4(); 125 | 126 | // 127 | 128 | this.projectVector = function ( vector, camera ) { 129 | 130 | console.warn( 'THREE.Projector: .projectVector() is now vector.project().' ); 131 | vector.project( camera ); 132 | 133 | }; 134 | 135 | this.unprojectVector = function ( vector, camera ) { 136 | 137 | console.warn( 'THREE.Projector: .unprojectVector() is now vector.unproject().' ); 138 | vector.unproject( camera ); 139 | 140 | }; 141 | 142 | this.pickingRay = function ( vector, camera ) { 143 | 144 | console.error( 'THREE.Projector: .pickingRay() is now raycaster.setFromCamera().' ); 145 | 146 | }; 147 | 148 | // 149 | 150 | var RenderList = function () { 151 | 152 | var normals = []; 153 | var uvs = []; 154 | 155 | var object = null; 156 | var material = null; 157 | 158 | var normalMatrix = new THREE.Matrix3(); 159 | 160 | var setObject = function ( value ) { 161 | 162 | object = value; 163 | material = object.material; 164 | 165 | normalMatrix.getNormalMatrix( object.matrixWorld ); 166 | 167 | normals.length = 0; 168 | uvs.length = 0; 169 | 170 | }; 171 | 172 | var projectVertex = function ( vertex ) { 173 | 174 | var position = vertex.position; 175 | var positionWorld = vertex.positionWorld; 176 | var positionScreen = vertex.positionScreen; 177 | 178 | positionWorld.copy( position ).applyMatrix4( _modelMatrix ); 179 | positionScreen.copy( positionWorld ).applyMatrix4( _viewProjectionMatrix ); 180 | 181 | var invW = 1 / positionScreen.w; 182 | 183 | positionScreen.x *= invW; 184 | positionScreen.y *= invW; 185 | positionScreen.z *= invW; 186 | 187 | vertex.visible = positionScreen.x >= - 1 && positionScreen.x <= 1 && 188 | positionScreen.y >= - 1 && positionScreen.y <= 1 && 189 | positionScreen.z >= - 1 && positionScreen.z <= 1; 190 | 191 | }; 192 | 193 | var pushVertex = function ( x, y, z ) { 194 | 195 | _vertex = getNextVertexInPool(); 196 | _vertex.position.set( x, y, z ); 197 | 198 | projectVertex( _vertex ); 199 | 200 | }; 201 | 202 | var pushNormal = function ( x, y, z ) { 203 | 204 | normals.push( x, y, z ); 205 | 206 | }; 207 | 208 | var pushUv = function ( x, y ) { 209 | 210 | uvs.push( x, y ); 211 | 212 | }; 213 | 214 | var checkTriangleVisibility = function ( v1, v2, v3 ) { 215 | 216 | if ( v1.visible === true || v2.visible === true || v3.visible === true ) return true; 217 | 218 | _points3[ 0 ] = v1.positionScreen; 219 | _points3[ 1 ] = v2.positionScreen; 220 | _points3[ 2 ] = v3.positionScreen; 221 | 222 | return _clipBox.isIntersectionBox( _boundingBox.setFromPoints( _points3 ) ); 223 | 224 | }; 225 | 226 | var checkBackfaceCulling = function ( v1, v2, v3 ) { 227 | 228 | return ( ( v3.positionScreen.x - v1.positionScreen.x ) * 229 | ( v2.positionScreen.y - v1.positionScreen.y ) - 230 | ( v3.positionScreen.y - v1.positionScreen.y ) * 231 | ( v2.positionScreen.x - v1.positionScreen.x ) ) < 0; 232 | 233 | }; 234 | 235 | var pushLine = function ( a, b ) { 236 | 237 | var v1 = _vertexPool[ a ]; 238 | var v2 = _vertexPool[ b ]; 239 | 240 | _line = getNextLineInPool(); 241 | 242 | _line.id = object.id; 243 | _line.v1.copy( v1 ); 244 | _line.v2.copy( v2 ); 245 | _line.z = ( v1.positionScreen.z + v2.positionScreen.z ) / 2; 246 | 247 | _line.material = object.material; 248 | 249 | _renderData.elements.push( _line ); 250 | 251 | }; 252 | 253 | var pushTriangle = function ( a, b, c ) { 254 | 255 | var v1 = _vertexPool[ a ]; 256 | var v2 = _vertexPool[ b ]; 257 | var v3 = _vertexPool[ c ]; 258 | 259 | if ( checkTriangleVisibility( v1, v2, v3 ) === false ) return; 260 | 261 | if ( material.side === THREE.DoubleSide || checkBackfaceCulling( v1, v2, v3 ) === true ) { 262 | 263 | _face = getNextFaceInPool(); 264 | 265 | _face.id = object.id; 266 | _face.v1.copy( v1 ); 267 | _face.v2.copy( v2 ); 268 | _face.v3.copy( v3 ); 269 | _face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3; 270 | 271 | for ( var i = 0; i < 3; i ++ ) { 272 | 273 | var offset = arguments[ i ] * 3; 274 | var normal = _face.vertexNormalsModel[ i ]; 275 | 276 | normal.set( normals[ offset ], normals[ offset + 1 ], normals[ offset + 2 ] ); 277 | normal.applyMatrix3( normalMatrix ).normalize(); 278 | 279 | var offset2 = arguments[ i ] * 2; 280 | 281 | var uv = _face.uvs[ i ]; 282 | uv.set( uvs[ offset2 ], uvs[ offset2 + 1 ] ); 283 | 284 | } 285 | 286 | _face.vertexNormalsLength = 3; 287 | 288 | _face.material = object.material; 289 | 290 | _renderData.elements.push( _face ); 291 | 292 | } 293 | 294 | }; 295 | 296 | return { 297 | setObject: setObject, 298 | projectVertex: projectVertex, 299 | checkTriangleVisibility: checkTriangleVisibility, 300 | checkBackfaceCulling: checkBackfaceCulling, 301 | pushVertex: pushVertex, 302 | pushNormal: pushNormal, 303 | pushUv: pushUv, 304 | pushLine: pushLine, 305 | pushTriangle: pushTriangle 306 | } 307 | 308 | }; 309 | 310 | var renderList = new RenderList(); 311 | 312 | this.projectScene = function ( scene, camera, sortObjects, sortElements ) { 313 | 314 | _faceCount = 0; 315 | _lineCount = 0; 316 | _spriteCount = 0; 317 | 318 | _renderData.elements.length = 0; 319 | 320 | if ( scene.autoUpdate === true ) scene.updateMatrixWorld(); 321 | if ( camera.parent === undefined ) camera.updateMatrixWorld(); 322 | 323 | _viewMatrix.copy( camera.matrixWorldInverse.getInverse( camera.matrixWorld ) ); 324 | _viewProjectionMatrix.multiplyMatrices( camera.projectionMatrix, _viewMatrix ); 325 | 326 | _frustum.setFromMatrix( _viewProjectionMatrix ); 327 | 328 | // 329 | 330 | _objectCount = 0; 331 | 332 | _renderData.objects.length = 0; 333 | _renderData.lights.length = 0; 334 | 335 | scene.traverseVisible( function ( object ) { 336 | 337 | if ( object instanceof THREE.Light ) { 338 | 339 | _renderData.lights.push( object ); 340 | 341 | } else if ( object instanceof THREE.Mesh || object instanceof THREE.Line || object instanceof THREE.Sprite ) { 342 | 343 | if ( object.material.visible === false ) return; 344 | 345 | if ( object.frustumCulled === false || _frustum.intersectsObject( object ) === true ) { 346 | 347 | _object = getNextObjectInPool(); 348 | _object.id = object.id; 349 | _object.object = object; 350 | 351 | _vector3.setFromMatrixPosition( object.matrixWorld ); 352 | _vector3.applyProjection( _viewProjectionMatrix ); 353 | _object.z = _vector3.z; 354 | 355 | _renderData.objects.push( _object ); 356 | 357 | } 358 | 359 | } 360 | 361 | } ); 362 | 363 | if ( sortObjects === true ) { 364 | 365 | _renderData.objects.sort( painterSort ); 366 | 367 | } 368 | 369 | // 370 | 371 | for ( var o = 0, ol = _renderData.objects.length; o < ol; o ++ ) { 372 | 373 | var object = _renderData.objects[ o ].object; 374 | var geometry = object.geometry; 375 | 376 | renderList.setObject( object ); 377 | 378 | _modelMatrix = object.matrixWorld; 379 | 380 | _vertexCount = 0; 381 | 382 | if ( object instanceof THREE.Mesh ) { 383 | 384 | if ( geometry instanceof THREE.BufferGeometry ) { 385 | 386 | var attributes = geometry.attributes; 387 | var offsets = geometry.offsets; 388 | 389 | if ( attributes.position === undefined ) continue; 390 | 391 | var positions = attributes.position.array; 392 | 393 | for ( var i = 0, l = positions.length; i < l; i += 3 ) { 394 | 395 | renderList.pushVertex( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); 396 | 397 | } 398 | 399 | if ( attributes.normal !== undefined ) { 400 | 401 | var normals = attributes.normal.array; 402 | 403 | for ( var i = 0, l = normals.length; i < l; i += 3 ) { 404 | 405 | renderList.pushNormal( normals[ i ], normals[ i + 1 ], normals[ i + 2 ] ); 406 | 407 | } 408 | 409 | } 410 | 411 | if ( attributes.uv !== undefined ) { 412 | 413 | var uvs = attributes.uv.array; 414 | 415 | for ( var i = 0, l = uvs.length; i < l; i += 2 ) { 416 | 417 | renderList.pushUv( uvs[ i ], uvs[ i + 1 ] ); 418 | 419 | } 420 | 421 | } 422 | 423 | if ( attributes.index !== undefined ) { 424 | 425 | var indices = attributes.index.array; 426 | 427 | if ( offsets.length > 0 ) { 428 | 429 | for ( var o = 0; o < offsets.length; o ++ ) { 430 | 431 | var offset = offsets[ o ]; 432 | var index = offset.index; 433 | 434 | for ( var i = offset.start, l = offset.start + offset.count; i < l; i += 3 ) { 435 | 436 | renderList.pushTriangle( indices[ i ] + index, indices[ i + 1 ] + index, indices[ i + 2 ] + index ); 437 | 438 | } 439 | 440 | } 441 | 442 | } else { 443 | 444 | for ( var i = 0, l = indices.length; i < l; i += 3 ) { 445 | 446 | renderList.pushTriangle( indices[ i ], indices[ i + 1 ], indices[ i + 2 ] ); 447 | 448 | } 449 | 450 | } 451 | 452 | } else { 453 | 454 | for ( var i = 0, l = positions.length / 3; i < l; i += 3 ) { 455 | 456 | renderList.pushTriangle( i, i + 1, i + 2 ); 457 | 458 | } 459 | 460 | } 461 | 462 | } else if ( geometry instanceof THREE.Geometry ) { 463 | 464 | var vertices = geometry.vertices; 465 | var faces = geometry.faces; 466 | var faceVertexUvs = geometry.faceVertexUvs[ 0 ]; 467 | 468 | _normalMatrix.getNormalMatrix( _modelMatrix ); 469 | 470 | var material = object.material; 471 | 472 | var isFaceMaterial = material instanceof THREE.MeshFaceMaterial; 473 | var objectMaterials = isFaceMaterial === true ? object.material : null; 474 | 475 | for ( var v = 0, vl = vertices.length; v < vl; v ++ ) { 476 | 477 | var vertex = vertices[ v ]; 478 | 479 | _vector3.copy( vertex ); 480 | 481 | if ( material.morphTargets === true ) { 482 | 483 | var morphTargets = geometry.morphTargets; 484 | var morphInfluences = object.morphTargetInfluences; 485 | 486 | for ( var t = 0, tl = morphTargets.length; t < tl; t ++ ) { 487 | 488 | var influence = morphInfluences[ t ]; 489 | 490 | if ( influence === 0 ) continue; 491 | 492 | var target = morphTargets[ t ]; 493 | var targetVertex = target.vertices[ v ]; 494 | 495 | _vector3.x += ( targetVertex.x - vertex.x ) * influence; 496 | _vector3.y += ( targetVertex.y - vertex.y ) * influence; 497 | _vector3.z += ( targetVertex.z - vertex.z ) * influence; 498 | 499 | } 500 | 501 | } 502 | 503 | renderList.pushVertex( _vector3.x, _vector3.y, _vector3.z ); 504 | 505 | } 506 | 507 | for ( var f = 0, fl = faces.length; f < fl; f ++ ) { 508 | 509 | var face = faces[ f ]; 510 | 511 | var material = isFaceMaterial === true 512 | ? objectMaterials.materials[ face.materialIndex ] 513 | : object.material; 514 | 515 | if ( material === undefined ) continue; 516 | 517 | var side = material.side; 518 | 519 | var v1 = _vertexPool[ face.a ]; 520 | var v2 = _vertexPool[ face.b ]; 521 | var v3 = _vertexPool[ face.c ]; 522 | 523 | if ( renderList.checkTriangleVisibility( v1, v2, v3 ) === false ) continue; 524 | 525 | var visible = renderList.checkBackfaceCulling( v1, v2, v3 ); 526 | 527 | if ( side !== THREE.DoubleSide ) { 528 | if ( side === THREE.FrontSide && visible === false ) continue; 529 | if ( side === THREE.BackSide && visible === true ) continue; 530 | } 531 | 532 | _face = getNextFaceInPool(); 533 | 534 | _face.id = object.id; 535 | _face.v1.copy( v1 ); 536 | _face.v2.copy( v2 ); 537 | _face.v3.copy( v3 ); 538 | 539 | _face.normalModel.copy( face.normal ); 540 | 541 | if ( visible === false && ( side === THREE.BackSide || side === THREE.DoubleSide ) ) { 542 | 543 | _face.normalModel.negate(); 544 | 545 | } 546 | 547 | _face.normalModel.applyMatrix3( _normalMatrix ).normalize(); 548 | 549 | var faceVertexNormals = face.vertexNormals; 550 | 551 | for ( var n = 0, nl = Math.min( faceVertexNormals.length, 3 ); n < nl; n ++ ) { 552 | 553 | var normalModel = _face.vertexNormalsModel[ n ]; 554 | normalModel.copy( faceVertexNormals[ n ] ); 555 | 556 | if ( visible === false && ( side === THREE.BackSide || side === THREE.DoubleSide ) ) { 557 | 558 | normalModel.negate(); 559 | 560 | } 561 | 562 | normalModel.applyMatrix3( _normalMatrix ).normalize(); 563 | 564 | } 565 | 566 | _face.vertexNormalsLength = faceVertexNormals.length; 567 | 568 | var vertexUvs = faceVertexUvs[ f ]; 569 | 570 | if ( vertexUvs !== undefined ) { 571 | 572 | for ( var u = 0; u < 3; u ++ ) { 573 | 574 | _face.uvs[ u ].copy( vertexUvs[ u ] ); 575 | 576 | } 577 | 578 | } 579 | 580 | _face.color = face.color; 581 | _face.material = material; 582 | 583 | _face.z = ( v1.positionScreen.z + v2.positionScreen.z + v3.positionScreen.z ) / 3; 584 | 585 | _renderData.elements.push( _face ); 586 | 587 | } 588 | 589 | } 590 | 591 | } else if ( object instanceof THREE.Line ) { 592 | 593 | if ( geometry instanceof THREE.BufferGeometry ) { 594 | 595 | var attributes = geometry.attributes; 596 | 597 | if ( attributes.position !== undefined ) { 598 | 599 | var positions = attributes.position.array; 600 | 601 | for ( var i = 0, l = positions.length; i < l; i += 3 ) { 602 | 603 | renderList.pushVertex( positions[ i ], positions[ i + 1 ], positions[ i + 2 ] ); 604 | 605 | } 606 | 607 | if ( attributes.index !== undefined ) { 608 | 609 | var indices = attributes.index.array; 610 | 611 | for ( var i = 0, l = indices.length; i < l; i += 2 ) { 612 | 613 | renderList.pushLine( indices[ i ], indices[ i + 1 ] ); 614 | 615 | } 616 | 617 | } else { 618 | 619 | var step = object.mode === THREE.LinePieces ? 2 : 1; 620 | 621 | for ( var i = 0, l = ( positions.length / 3 ) - 1; i < l; i += step ) { 622 | 623 | renderList.pushLine( i, i + 1 ); 624 | 625 | } 626 | 627 | } 628 | 629 | } 630 | 631 | } else if ( geometry instanceof THREE.Geometry ) { 632 | 633 | _modelViewProjectionMatrix.multiplyMatrices( _viewProjectionMatrix, _modelMatrix ); 634 | 635 | var vertices = object.geometry.vertices; 636 | 637 | if ( vertices.length === 0 ) continue; 638 | 639 | v1 = getNextVertexInPool(); 640 | v1.positionScreen.copy( vertices[ 0 ] ).applyMatrix4( _modelViewProjectionMatrix ); 641 | 642 | // Handle LineStrip and LinePieces 643 | var step = object.mode === THREE.LinePieces ? 2 : 1; 644 | 645 | for ( var v = 1, vl = vertices.length; v < vl; v ++ ) { 646 | 647 | v1 = getNextVertexInPool(); 648 | v1.positionScreen.copy( vertices[ v ] ).applyMatrix4( _modelViewProjectionMatrix ); 649 | 650 | if ( ( v + 1 ) % step > 0 ) continue; 651 | 652 | v2 = _vertexPool[ _vertexCount - 2 ]; 653 | 654 | _clippedVertex1PositionScreen.copy( v1.positionScreen ); 655 | _clippedVertex2PositionScreen.copy( v2.positionScreen ); 656 | 657 | if ( clipLine( _clippedVertex1PositionScreen, _clippedVertex2PositionScreen ) === true ) { 658 | 659 | // Perform the perspective divide 660 | _clippedVertex1PositionScreen.multiplyScalar( 1 / _clippedVertex1PositionScreen.w ); 661 | _clippedVertex2PositionScreen.multiplyScalar( 1 / _clippedVertex2PositionScreen.w ); 662 | 663 | _line = getNextLineInPool(); 664 | 665 | _line.id = object.id; 666 | _line.v1.positionScreen.copy( _clippedVertex1PositionScreen ); 667 | _line.v2.positionScreen.copy( _clippedVertex2PositionScreen ); 668 | 669 | _line.z = Math.max( _clippedVertex1PositionScreen.z, _clippedVertex2PositionScreen.z ); 670 | 671 | _line.material = object.material; 672 | 673 | if ( object.material.vertexColors === THREE.VertexColors ) { 674 | 675 | _line.vertexColors[ 0 ].copy( object.geometry.colors[ v ] ); 676 | _line.vertexColors[ 1 ].copy( object.geometry.colors[ v - 1 ] ); 677 | 678 | } 679 | 680 | _renderData.elements.push( _line ); 681 | 682 | } 683 | 684 | } 685 | 686 | } 687 | 688 | } else if ( object instanceof THREE.Sprite ) { 689 | 690 | _vector4.set( _modelMatrix.elements[ 12 ], _modelMatrix.elements[ 13 ], _modelMatrix.elements[ 14 ], 1 ); 691 | _vector4.applyMatrix4( _viewProjectionMatrix ); 692 | 693 | var invW = 1 / _vector4.w; 694 | 695 | _vector4.z *= invW; 696 | 697 | if ( _vector4.z >= - 1 && _vector4.z <= 1 ) { 698 | 699 | _sprite = getNextSpriteInPool(); 700 | _sprite.id = object.id; 701 | _sprite.x = _vector4.x * invW; 702 | _sprite.y = _vector4.y * invW; 703 | _sprite.z = _vector4.z; 704 | _sprite.object = object; 705 | 706 | _sprite.rotation = object.rotation; 707 | 708 | _sprite.scale.x = object.scale.x * Math.abs( _sprite.x - ( _vector4.x + camera.projectionMatrix.elements[ 0 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 12 ] ) ); 709 | _sprite.scale.y = object.scale.y * Math.abs( _sprite.y - ( _vector4.y + camera.projectionMatrix.elements[ 5 ] ) / ( _vector4.w + camera.projectionMatrix.elements[ 13 ] ) ); 710 | 711 | _sprite.material = object.material; 712 | 713 | _renderData.elements.push( _sprite ); 714 | 715 | } 716 | 717 | } 718 | 719 | } 720 | 721 | if ( sortElements === true ) { 722 | 723 | _renderData.elements.sort( painterSort ); 724 | 725 | } 726 | 727 | return _renderData; 728 | 729 | }; 730 | 731 | // Pools 732 | 733 | function getNextObjectInPool() { 734 | 735 | if ( _objectCount === _objectPoolLength ) { 736 | 737 | var object = new THREE.RenderableObject(); 738 | _objectPool.push( object ); 739 | _objectPoolLength ++; 740 | _objectCount ++; 741 | return object; 742 | 743 | } 744 | 745 | return _objectPool[ _objectCount ++ ]; 746 | 747 | } 748 | 749 | function getNextVertexInPool() { 750 | 751 | if ( _vertexCount === _vertexPoolLength ) { 752 | 753 | var vertex = new THREE.RenderableVertex(); 754 | _vertexPool.push( vertex ); 755 | _vertexPoolLength ++; 756 | _vertexCount ++; 757 | return vertex; 758 | 759 | } 760 | 761 | return _vertexPool[ _vertexCount ++ ]; 762 | 763 | } 764 | 765 | function getNextFaceInPool() { 766 | 767 | if ( _faceCount === _facePoolLength ) { 768 | 769 | var face = new THREE.RenderableFace(); 770 | _facePool.push( face ); 771 | _facePoolLength ++; 772 | _faceCount ++; 773 | return face; 774 | 775 | } 776 | 777 | return _facePool[ _faceCount ++ ]; 778 | 779 | 780 | } 781 | 782 | function getNextLineInPool() { 783 | 784 | if ( _lineCount === _linePoolLength ) { 785 | 786 | var line = new THREE.RenderableLine(); 787 | _linePool.push( line ); 788 | _linePoolLength ++; 789 | _lineCount ++ 790 | return line; 791 | 792 | } 793 | 794 | return _linePool[ _lineCount ++ ]; 795 | 796 | } 797 | 798 | function getNextSpriteInPool() { 799 | 800 | if ( _spriteCount === _spritePoolLength ) { 801 | 802 | var sprite = new THREE.RenderableSprite(); 803 | _spritePool.push( sprite ); 804 | _spritePoolLength ++; 805 | _spriteCount ++ 806 | return sprite; 807 | 808 | } 809 | 810 | return _spritePool[ _spriteCount ++ ]; 811 | 812 | } 813 | 814 | // 815 | 816 | function painterSort( a, b ) { 817 | 818 | if ( a.z !== b.z ) { 819 | 820 | return b.z - a.z; 821 | 822 | } else if ( a.id !== b.id ) { 823 | 824 | return a.id - b.id; 825 | 826 | } else { 827 | 828 | return 0; 829 | 830 | } 831 | 832 | } 833 | 834 | function clipLine( s1, s2 ) { 835 | 836 | var alpha1 = 0, alpha2 = 1, 837 | 838 | // Calculate the boundary coordinate of each vertex for the near and far clip planes, 839 | // Z = -1 and Z = +1, respectively. 840 | bc1near = s1.z + s1.w, 841 | bc2near = s2.z + s2.w, 842 | bc1far = - s1.z + s1.w, 843 | bc2far = - s2.z + s2.w; 844 | 845 | if ( bc1near >= 0 && bc2near >= 0 && bc1far >= 0 && bc2far >= 0 ) { 846 | 847 | // Both vertices lie entirely within all clip planes. 848 | return true; 849 | 850 | } else if ( ( bc1near < 0 && bc2near < 0 ) || ( bc1far < 0 && bc2far < 0 ) ) { 851 | 852 | // Both vertices lie entirely outside one of the clip planes. 853 | return false; 854 | 855 | } else { 856 | 857 | // The line segment spans at least one clip plane. 858 | 859 | if ( bc1near < 0 ) { 860 | 861 | // v1 lies outside the near plane, v2 inside 862 | alpha1 = Math.max( alpha1, bc1near / ( bc1near - bc2near ) ); 863 | 864 | } else if ( bc2near < 0 ) { 865 | 866 | // v2 lies outside the near plane, v1 inside 867 | alpha2 = Math.min( alpha2, bc1near / ( bc1near - bc2near ) ); 868 | 869 | } 870 | 871 | if ( bc1far < 0 ) { 872 | 873 | // v1 lies outside the far plane, v2 inside 874 | alpha1 = Math.max( alpha1, bc1far / ( bc1far - bc2far ) ); 875 | 876 | } else if ( bc2far < 0 ) { 877 | 878 | // v2 lies outside the far plane, v2 inside 879 | alpha2 = Math.min( alpha2, bc1far / ( bc1far - bc2far ) ); 880 | 881 | } 882 | 883 | if ( alpha2 < alpha1 ) { 884 | 885 | // The line segment spans two boundaries, but is outside both of them. 886 | // (This can't happen when we're only clipping against just near/far but good 887 | // to leave the check here for future usage if other clip planes are added.) 888 | return false; 889 | 890 | } else { 891 | 892 | // Update the s1 and s2 vertices to match the clipped line segment. 893 | s1.lerp( s2, alpha1 ); 894 | s2.lerp( s1, 1 - alpha2 ); 895 | 896 | return true; 897 | 898 | } 899 | 900 | } 901 | 902 | } 903 | 904 | }; 905 | -------------------------------------------------------------------------------- /public/web/netflix.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/web/nf.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/youshandefeiyang/sub-web-modify/68422ba384475acb791fd65eb30ada7f38634a6b/public/web/nf.ico -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/assets/css/dark.min.css: -------------------------------------------------------------------------------- 1 | .dark-mode .channel{display:inline-block!important}.dark-mode .el-tabs__item:hover,.dark-mode .el-tabs__item.is-active{color:#409eff}.dark-mode .el-tabs__item,.dark-mode .el-dialog__title{color:#fff}.dark-mode .el-card{background:0 0!important}.dark-mode .el-popover{background:#000!important}.dark-mode .el-dialog{border:1px solid #313131!important}.dark-mode .el-message-box__title,.dark-mode .el-message-box__content{color:#fff!important}.dark-mode .el-collapse-item__content{padding-bottom:0!important}.dark-mode #canvas,dark-mode .canvas-wrap{display:none!important}.dark-mode .el-message-box{background-color:#000!important}.dark-mode #yejian,.dark-mode .bilibili,.dark-mode .dianbao,.dark-mode .youguan{display:none!important}.dark-mode .el-dialog__body,.dark-mode .el-dialog__footer,.dark-mode .el-dialog__header{background-color:#000!important;color:#fff!important}.dark-mode .el-input__count{color:#fff!important;background:rgba(128,128,128,.7)!important}.dark-mode .el-message-box__headerbtn .el-message-box__close{color:#fff!important}.dark-mode .el-textarea{background-color:rgba(255,255,255,0)!important;color:#fff!important}.dark-mode .el-notification__closeBtn,.dark-mode .el-notification__title,.dark-mode .el-notification__content,.dark-mode .el-message .el-icon-error,.dark-mode .el-message .el-icon-success,.dark-mode .el-notification .el-icon-warning,.dark-mode .el-message .el-icon-warning,.dark-mode .el-message--error .el-message__content,.dark-mode .el-message--success .el-message__content,.dark-mode .el-message--warning .el-message__content{color:#fff!important}.dark-mode .el-notification,.dark-mode .el-message--error,.dark-mode .el-message--success,.dark-mode .el-message--warning{background-color:rgb(0,0,0,.7)!important;border-color:#fff!important}.dark-mode .el-button--zhuti{color:#fff!important;font-size:14px!important;border:0!important;width:54px!important;padding:0 0!important;background-color:#000!important}.dark-mode .el-button--zhuti:active,.dark-mode .el-button--zhuti:focus,.dark-mode .el-button--zhuti:hover{color:#fff!important}.dark-mode,.dark-mode .el-button--danger,.dark-mode .el-button--primary,.dark-mode .el-button--success,.dark-mode .el-button--warning{background-color:#000!important}.dark-mode .el-collapse-item .el-button--limr{color:#fff!important;background:rgba(255,255,255,.5)!important;border-radius:4px!important;height:32px!important}.dark-mode .el-button--limr:focus,.dark-mode .el-button--limr:hover,.dark-mode .el-collapse-item .el-button--limr:active{color:#fff!important;border-color:#dcdfe6!important}.dark-mode .el-collapse-item__wrap{background:0 0!important;border-bottom:none!important}.dark-mode .el-collapse{border-top:none!important;border-bottom:none!important}.dark-mode .el-collapse-item__header{background:0 0!important;border-bottom:none!important;height:auto!important}.dark-mode .el-collapse-item__arrow{display:none!important}.dark-mode .el-card__header{background:rgba(0,0,0,.8)!important;position:relative;color:#fff}.dark-mode .el-card__body{background:rgba(0,0,0,.8);color:#000}.dark-mode .el-checkbox,.dark-mode .el-form-item__label,.dark-mode .el-input-group__append,.dark-mode .el-input__inner,.dark-mode .el-radio,.dark-mode .el-select .el-input .el-select__caret,.dark-mode .el-textarea__inner{color:#fff!important}.dark-mode .el-divider{background:rgba(255,255,255,.5)!important}.dark-mode .el-divider__text{background-color:#000!important;color:#fff!important}.dark-mode .el-input__inner,.dark-mode .el-textarea__inner{background:rgba(255,255,255,.5)!important}.dark-mode .el-input.is-active .el-input__inner,.dark-mode .el-input__inner:focus,.dark-mode .el-select .el-input.is-focus .el-input__inner,.dark-mode .el-textarea__inner:focus{border-color:#999!important}.dark-mode .el-checkbox.is-bordered,.dark-mode .el-checkbox__inner{border:1px solid #fff!important}.dark-mode .el-checkbox.is-bordered.is-checked{border-color:#fff!important}.dark-mode .el-radio__input.is-checked+.el-radio__label{color:#000!important}.dark-mode .el-radio__input.is-checked .el-radio__inner{border-color:#000!important;background:#000!important}.dark-mode .el-checkbox__input.is-checked .el-checkbox__inner,.dark-mode .el-checkbox__input.is-indeterminate .el-checkbox__inner{background-color:#000!important}.dark-mode .el-checkbox__input.is-checked+.el-checkbox__label{color:#fff!important}.dark-mode .el-row--flex .el-checkbox__input.is-checked+.el-checkbox__label{color:#fff!important}.dark-mode .el-row--flex .el-checkbox__input.is-checked .el-checkbox__inner,.dark-mode .el-row--flex .el-checkbox__input.is-indeterminate .el-checkbox__inner{background-color:#000!important;border-color:#fff!important}.dark-mode .el-row--flex .el-checkbox__inner:hover{border-color:#000!important}.dark-mode .el-row--flex .el-checkbox__input.is-focus .el-checkbox__inner{border-color:#fff!important}.dark-mode .el-checkbox.is-bordered.is-checked{background:rgba(0,0,0,.88)!important;color:#fff!important}.dark-mode .el-row span .el-button{background:0 0!important;border:1px solid #fff!important;color:#fff!important}.dark-mode .el-row span .el-button:focus,.dark-mode .el-row span .el-button:hover{color:#fff!important;background:rgba(0,0,0,.88)!important}.dark-mode .el-divider__text{border:1px solid rgba(0,0,0,.4);border-radius:3px}.dark-mode .el-select-dropdown__item.selected{background:#333!important;color:#fff!important}.dark-mode .el-input.is-disabled .el-input__inner{border:none}.dark-mode .el-form-item:last-child .el-button--primary{background-color:#303133!important;border-color:#303133!important}.dark-mode .el-form-item:last-child .el-button--primary:focus,.dark-mode .el-form-item:last-child .el-button--primary:hover{background:#666!important;border-color:#666!important}.dark-mode .el-form-item:last-child .el-button--primary.is-disabled{background:#999!important;border-color:#999!important}.dark-mode .el-row--flex{align-items:center;flex-wrap:wrap;justify-content:space-between}.dark-mode .el-row--flex .el-col-24{width:auto!important;margin:5px 0}.dark-mode .el-select-group__title{color:#999!important;background:rgba(0,0,0,.1);font-weight:600}.dark-mode .el-input-group__append button.el-button{border-color:#303133!important;background-color:#303133!important;color:#fff!important}.dark-mode .el-input-group__append,.dark-mode .el-input-group__prepend{border-color:#303133!important;background-color:#303133!important}.dark-mode .el-input__inner::-webkit-input-placeholder,.dark-mode .el-textarea__inner::-webkit-input-placeholder{color:rgba(255,255,255,.6)!important}.dark-mode .el-input__inner:-moz-placeholder,.dark-mode .el-textarea__inner:-moz-placeholder{color:rgba(255,255,255,.6)!important}.dark-mode .el-input__inner::-moz-placeholder,.dark-mode .el-textarea__inner::-moz-placeholder{color:rgba(255,255,255,.6)!important}.dark-mode .el-input__inner:-ms-input-placeholder,.dark-mode .el-textarea__inner:-ms-input-placeholder{color:rgba(255,255,255,.6)!important}.dark-mode .el-popover.el-popper .el-row:focus,.dark-mode .el-popover.el-popper .popper__arrow{outline:0}.dark-mode .el-radio:focus:not(.is-focus):not(:active):not(.is-disabled) .el-radio__inner{box-shadow:none!important}.dark-mode .el-radio__inner:hover{border-color:#000!important}.dark-mode .el-radio__inner{border:1px solid #999!important}.dark-mode .dialog-footer .el-button{background:0 0!important;border:1px solid #000!important;color:#000!important}.dark-mode .dialog-footer .el-button:focus,.dark-mode .dialog-footer .el-button:hover{color:#fff!important;background:#303133!important;border:1px solid #303133!important}.dark-mode .dialog-footer .el-button--primary{background-color:#303133!important;border-color:#303133!important;color:#fff!important}.dark-mode .dialog-footer .el-button--primary:focus,.dark-mode .dialog-footer .el-button--primary:hover{background:#666!important;border-color:#666!important}.dark-mode .dialog-footer .el-button--primary.is-disabled,.dark-mode .dialog-footer .el-button--primary.is-disabled:active,.dark-mode .dialog-footer .el-button--primary.is-disabled:focus,.dark-mode .dialog-footer .el-button--primary.is-disabled:hover{background:#999!important;border-color:#999!important;color:#fff!important}.dark-mode .el-link.el-link--primary{color:#fff!important}.dark-mode .el-link.el-link--primary:hover{color:#fff!important}.dark-mode .el-link.el-link--default:after,.dark-mode .el-link.el-link--primary.is-underline:hover:after,.dark-mode .el-link.el-link--primary:after{border-color:#000!important} 2 | -------------------------------------------------------------------------------- /src/assets/css/element-ui.scss: -------------------------------------------------------------------------------- 1 | // cover some element-ui styles 2 | 3 | .el-breadcrumb__inner, 4 | .el-breadcrumb__inner a { 5 | font-weight: 400 !important; 6 | } 7 | 8 | .el-upload { 9 | input[type="file"] { 10 | display: none !important; 11 | } 12 | } 13 | 14 | .el-upload__input { 15 | display: none; 16 | } 17 | 18 | .cell { 19 | .el-tag { 20 | margin-right: 0px; 21 | } 22 | } 23 | 24 | .small-padding { 25 | .cell { 26 | padding-left: 5px; 27 | padding-right: 5px; 28 | } 29 | } 30 | 31 | .fixed-width { 32 | .el-button--mini { 33 | padding: 7px 10px; 34 | width: 60px; 35 | } 36 | } 37 | 38 | .status-col { 39 | .cell { 40 | padding: 0 10px; 41 | text-align: center; 42 | 43 | .el-tag { 44 | margin-right: 0px; 45 | } 46 | } 47 | } 48 | 49 | // to fixed https://github.com/ElemeFE/element/issues/2461 50 | .el-dialog { 51 | transform: none; 52 | left: 0; 53 | position: relative; 54 | margin: 0 auto; 55 | } 56 | 57 | // refine element ui upload 58 | .upload-container { 59 | .el-upload { 60 | width: 100%; 61 | 62 | .el-upload-dragger { 63 | width: 100%; 64 | height: 200px; 65 | } 66 | } 67 | } 68 | 69 | // dropdown 70 | .el-dropdown-menu { 71 | a { 72 | display: block 73 | } 74 | } 75 | 76 | // fix date-picker ui bug in filter-item 77 | .el-range-editor.el-input__inner { 78 | display: inline-flex !important; 79 | } -------------------------------------------------------------------------------- /src/assets/css/element-variables.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * I think element-ui's default theme color is too light for long-term use. 3 | * So I modified the default color and you can modify it to your liking. 4 | **/ 5 | 6 | /* theme color */ 7 | $--color-primary: #304156; 8 | $--color-success: #65C934; 9 | $--color-warning: #E6A23C; 10 | $--color-danger: #F56C6C; 11 | // $--color-info: #1E1E1E; 12 | 13 | $--button-font-weight: 400; 14 | 15 | // $--color-text-regular: #1f2d3d; 16 | 17 | $--border-color-light: #dfe4ed; 18 | $--border-color-lighter: #e6ebf5; 19 | 20 | $--table-border:1px solid#dfe6ec; 21 | 22 | /* icon font path, required */ 23 | $--font-path: '~element-ui/lib/theme-chalk/fonts'; 24 | 25 | @import "~element-ui/packages/theme-chalk/src/index"; 26 | 27 | // the :export directive is the magic sauce for webpack 28 | // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass 29 | :export { 30 | theme: $--color-primary; 31 | } -------------------------------------------------------------------------------- /src/assets/css/light.min.css: -------------------------------------------------------------------------------- 1 | *{touch-action:manipulation}html{margin:0;padding:0}.el-input__count{color:#000!important;background:rgba(255,255,255,.7)!important}.el-select-dropdown{max-width:80%}.eldiy .el-form-item__content{line-height:44px!important}.el-checkbox.is-bordered.el-checkbox--small .el-checkbox__label{line-height:20px!important}.el-divider__text{padding:0!important}.el-link.is-underline:hover:after{border-bottom:0!important}.light-mode #rijian,.light-mode .channel,#canvas-show{display:none}.light-mode #canvas-show{display:inline!important}.light-mode .el-button--zhuti{font-size:14px!important;border:0!important;width:54px!important;padding:0 0!important}.light-mode .el-button--zhuti:active,.light-mode .el-button--zhuti:focus,.light-mode .el-button--zhuti:hover{color:#000!important;background-color:#fff!important}.light-mode #canvas{width:100%;height:2160px;overflow:hidden;position:absolute}.light-mode .canvas-wrap{width:100%;height:100%;position:fixed;left:0;top:0}a,b,div,i,img,input,label,li,p,select,span,textarea,ul{outline:0!important}textarea{-webkit-appearance:none;appearance:none}#app .el-row:not(.el-row--flex){margin:0 auto!important;padding:16px;max-width:1032px}.el-message-box__headerbtn .el-message-box__close,.light-mode .el-notification__closeBtn,.light-mode .el-notification__title,.light-mode .el-notification__content,.light-mode .el-message .el-icon-error,.light-mode .el-message .el-icon-success,.light-mode .el-notification .el-icon-warning,.light-mode .el-message .el-icon-warning,.light-mode .el-message--error .el-message__content,.light-mode .el-message--success .el-message__content,.light-mode .el-message--warning .el-message__content{color:#000!important}.light-mode .el-notification,.light-mode .el-message--error,.light-mode .el-message--success,.light-mode .el-message--warning{background-color:rgba(255,255,255,.7)!important;border-color:#fff!important}.light-mode .el-card{background:0 0!important}.light-mode .el-collapse-item__content{padding-bottom:0!important}.light-mode .el-collapse-item .el-button--limr{background:rgba(255,255,255,.5)!important;border-radius:4px!important;height:32px!important}.light-mode .el-button--limr:focus,.light-mode .el-button--limr:hover,.light-mode .el-collapse-item .el-button--limr:active{color:#606266!important;border-color:#dcdfe6!important}.light-mode .el-tabs--card>.el-tabs__header .el-tabs__item.is-active{border-bottom-color:transparent}.light-mode .el-collapse-item__wrap{background:0 0!important;border-bottom:none!important}.light-mode .el-collapse{border-top:none!important;border-bottom:none!important}.light-mode .el-collapse-item__header{background:0 0!important;border-bottom:none!important;height:auto!important}.light-mode .el-collapse-item__arrow{display:none!important}.light-mode .el-card__header{position:relative;color:#fff}.light-mode .el-card__body{background:rgba(255,255,255,.8);color:#000}.light-mode .el-form-item__label,.light-mode .el-input-group__append,.light-mode .el-input__inner,.light-mode .el-radio,.light-mode .el-select .el-input .el-select__caret,.light-mode .el-textarea__inner{color:#000!important}.light-mode .el-divider{background:rgba(0,0,0,.2)!important}.light-mode .el-input__inner,.light-mode .el-textarea__inner{background:rgba(255,255,255,.5)!important}.light-mode .el-input.is-active .el-input__inner,.light-mode .el-input__inner:focus,.light-mode .el-select .el-input.is-focus .el-input__inner,.light-mode .el-textarea__inner:focus{border-color:#999!important}.light-mode .el-checkbox.is-bordered,.light-mode .el-checkbox__inner{border:1px solid #000!important}.light-mode .el-checkbox{color:#000!important}.light-mode .el-checkbox.is-bordered.is-checked{border-color:#000!important}.light-mode .el-radio__input.is-checked+.el-radio__label{color:#000!important}.light-mode .el-radio__input.is-checked .el-radio__inner{border-color:#000!important;background:#000!important}.light-mode .el-checkbox__input.is-checked .el-checkbox__inner,.light-mode .el-checkbox__input.is-indeterminate .el-checkbox__inner{background-color:#000!important}.light-mode .el-checkbox__input.is-checked+.el-checkbox__label{color:#000!important}.light-mode .el-row--flex .el-checkbox__input.is-checked+.el-checkbox__label{color:#fff!important}.light-mode .el-row--flex .el-checkbox__input.is-checked .el-checkbox__inner,.light-mode .el-row--flex .el-checkbox__input.is-indeterminate .el-checkbox__inner{background-color:#000!important;border-color:#fff!important}.light-mode .el-row--flex .el-checkbox__inner:hover{border-color:#000!important}.light-mode .el-row--flex .el-checkbox__input.is-focus .el-checkbox__inner{border-color:#fff!important}.light-mode .el-checkbox.is-bordered.is-checked{background:rgba(0,0,0,.88)!important;color:#fff!important}.light-mode .el-row span .el-button{background:0 0!important;border:1px solid #000!important;color:#000!important}.light-mode .el-row span .el-button:focus,.light-mode .el-row span .el-button:hover{color:#fff!important;background:rgba(0,0,0,.88)!important}.light-mode .el-divider__text{border:1px solid rgba(0,0,0,.4);border-radius:3px}.light-mode .el-select-dropdown__item.selected{background:#333!important;color:#fff!important}.light-mode .el-input.is-disabled .el-input__inner{border:none}.light-mode .el-form-item:last-child .el-button--primary{background-color:#303133!important;border-color:#303133!important}.light-mode .el-form-item:last-child .el-button--primary:focus,.light-mode .el-form-item:last-child .el-button--primary:hover{background:#666!important;border-color:#666!important}.light-mode .el-form-item:last-child .el-button--primary.is-disabled{background:#999!important;border-color:#999!important}.light-mode .el-row--flex{align-items:center;flex-wrap:wrap;justify-content:space-between}.light-mode .el-row--flex .el-col-24{width:auto!important;margin:5px 0}.light-mode .el-select-group__title{color:#999!important;background:rgba(0,0,0,.1);font-weight:600}.light-mode .el-input-group__append button.el-button{border-color:transparent!important;background-color:#303133!important;color:#fff!important}.light-mode .el-input__inner::-webkit-input-placeholder,.light-mode .el-textarea__inner::-webkit-input-placeholder{color:#777!important}.light-mode .el-input__inner:-moz-placeholder,.light-mode .el-textarea__inner:-moz-placeholder{color:#777!important}.light-mode .el-input__inner::-moz-placeholder,.light-mode .el-textarea__inner::-moz-placeholder{color:#777!important}.light-mode .el-input__inner:-ms-input-placeholder,.light-mode .el-textarea__inner:-ms-input-placeholder{color:#777!important}.light-mode .el-popover.el-popper .el-row:focus,.light-mode .el-popover.el-popper .popper__arrow{outline:0}.light-mode .el-radio:focus:not(.is-focus):not(:active):not(.is-disabled) .el-radio__inner{box-shadow:none!important}.light-mode .el-radio__inner:hover{border-color:#000!important}.light-mode .el-radio__inner{border:1px solid #999!important}.light-mode .dialog-footer .el-button{background:0 0!important;border:1px solid #000!important;color:#000!important}.light-mode .dialog-footer .el-button:focus,.light-mode .dialog-footer .el-button:hover{color:#fff!important;background:#303133!important;border:1px solid #303133!important}.light-mode .dialog-footer .el-button--primary{background-color:#303133!important;border-color:#303133!important;color:#fff!important}.light-mode .dialog-footer .el-button--primary:focus,.light-mode .dialog-footer .el-button--primary:hover{background:#666!important;border-color:#666!important}.light-mode .dialog-footer .el-button--primary.is-disabled,.light-mode .dialog-footer .el-button--primary.is-disabled:active,.light-mode .dialog-footer .el-button--primary.is-disabled:focus,.light-mode .dialog-footer .el-button--primary.is-disabled:hover{background:#999!important;border-color:#999!important;color:#fff!important}.light-mode .el-link.el-link--primary{color:#333!important}.light-mode .el-link.el-link--primary:hover{color:#000!important}.light-mode .el-link.el-link--default:after,.light-mode .el-link.el-link--primary.is-underline:hover:after,.light-mode .el-link.el-link--primary:after{border-color:#000!important}@media(max-width:460px){.msgbox {width:80%!important}} 2 | -------------------------------------------------------------------------------- /src/components/SvgIcon/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 34 | 35 | 44 | -------------------------------------------------------------------------------- /src/icons/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import SvgIcon from '@/components/SvgIcon'// svg component 3 | 4 | // register globally 5 | Vue.component('svg-icon', SvgIcon) 6 | 7 | const req = require.context('./svg', false, /\.svg$/) 8 | const requireAll = requireContext => requireContext.keys().map(requireContext) 9 | requireAll(req) 10 | -------------------------------------------------------------------------------- /src/icons/svg/bilibili.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/github.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/telegram.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/icons/svg/youtube.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import router from './router' 4 | require(`@/plugins/element-ui`) 5 | require(`@/plugins/clipboard`) 6 | require(`@/plugins/base64`) 7 | require(`@/plugins/axios`) 8 | require(`@/plugins/device`) 9 | require(`@/plugins/particles`) 10 | 11 | 12 | import 'element-ui/lib/theme-chalk/index.css' 13 | import './assets/css/light.min.css' 14 | import './assets/css/dark.min.css' 15 | import '@/icons' // icon 16 | 17 | Vue.config.productionTip = false 18 | 19 | new Vue({ 20 | router, 21 | render: h => h(App) 22 | }).$mount('#app') 23 | -------------------------------------------------------------------------------- /src/plugins/axios.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import axios from "axios" 3 | 4 | axios.defaults.timeout = 5000 //请求超时的时间设定 5 | 6 | Vue.prototype.$axios = axios -------------------------------------------------------------------------------- /src/plugins/base64.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import btoa from 'btoa' 3 | import atob from 'atob' 4 | 5 | Vue.prototype.$btoa = (string) => btoa(string) 6 | Vue.prototype.$atob = (string) => atob(string) 7 | -------------------------------------------------------------------------------- /src/plugins/clipboard.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import clipboard from 'vue-clipboard2' 3 | 4 | Vue.use(clipboard) 5 | -------------------------------------------------------------------------------- /src/plugins/device.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | Vue.prototype.$getOS = () => { 4 | let ua = navigator.userAgent, 5 | isWindowsPhone = /(?:Windows Phone)/.test(ua), 6 | isSymbian = /(?:SymbianOS)/.test(ua) || isWindowsPhone, 7 | isAndroid = /(?:Android)/.test(ua), 8 | isFireFox = /(?:Firefox)/.test(ua), 9 | // isChrome = /(?:Chrome|CriOS)/.test(ua), 10 | isTablet = /(?:iPad|PlayBook)/.test(ua) || (isAndroid && !/(?:Mobile)/.test(ua)) || (isFireFox && /(?:Tablet)/.test(ua)), 11 | isIPhone = /(?:iPhone)/.test(ua) && !isTablet, 12 | isPc = !isIPhone && !isAndroid && !isSymbian && !isWindowsPhone; 13 | 14 | return { 15 | isTablet: isTablet, 16 | isIPhone: isIPhone, 17 | isAndroid: isAndroid, 18 | isPc: isPc 19 | }; 20 | } 21 | -------------------------------------------------------------------------------- /src/plugins/element-ui.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Element from 'element-ui' 3 | import locale from 'element-ui/lib/locale/lang/zh-CN' 4 | // import '@/assets/css/element-ui.scss' 5 | // import '@/assets/css/element-element-variables.scss' 6 | 7 | Vue.use(Element, { 8 | locale, 9 | size: 'small' 10 | }) 11 | 12 | Vue.use(Element.Loading.directive); 13 | 14 | Vue.prototype.$loading = Element.Loading.service; 15 | Vue.prototype.$msgbox = Element.MessageBox; 16 | Vue.prototype.$alert = Element.MessageBox.alert; 17 | Vue.prototype.$confirm = Element.MessageBox.confirm; 18 | Vue.prototype.$prompt = Element.MessageBox.prompt; 19 | Vue.prototype.$notify = Element.Notification; 20 | Vue.prototype.$message = Element.Message; 21 | -------------------------------------------------------------------------------- /src/plugins/particles.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueParticles from 'vue-particles' 3 | 4 | Vue.use(VueParticles) 5 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import VueRouter from "vue-router"; 3 | 4 | Vue.use(VueRouter); 5 | 6 | const routes = [ 7 | { 8 | path: "/", 9 | name: "SubConverter", 10 | component: () => import("../views/Subconverter.vue") 11 | } 12 | ]; 13 | 14 | const router = new VueRouter({ 15 | mode: "history", 16 | base: process.env.BASE_URL, 17 | routes 18 | }); 19 | 20 | export default router; 21 | -------------------------------------------------------------------------------- /src/views/Subconverter.vue: -------------------------------------------------------------------------------- 1 | 423 | 1416 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | function resolve(dir) { 4 | return path.join(__dirname, dir) 5 | } 6 | 7 | module.exports = { 8 | css: { 9 | loaderOptions: { 10 | less: { 11 | javascriptEnabled: true 12 | } 13 | } 14 | }, 15 | 16 | chainWebpack: config => { 17 | // set svg-sprite-loader 18 | config.module 19 | .rule('svg') 20 | .exclude.add(resolve('src/icons')) 21 | .end() 22 | config.module 23 | .rule('icons') 24 | .test(/\.svg$/) 25 | .include.add(resolve('src/icons')) 26 | .end() 27 | .use('svg-sprite-loader') 28 | .loader('svg-sprite-loader') 29 | .options({ 30 | symbolId: 'icon-[name]' 31 | }) 32 | .end() 33 | } 34 | }; --------------------------------------------------------------------------------