diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..3f6e4d4
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,17 @@
+# top-most EditorConfig file
+root = true
+
+# Base rules
+[*]
+#改行コード
+end_of_line = lf
+#ファイルの末尾が改行文字ではない場合に補完
+insert_final_newline = false
+indent_style = space
+indent_size = 2
+charset = utf-8
+#行末の空白文字を削除
+trim_trailing_whitespace = true
+
+[*.md]
+insert_final_newline = true
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a153747
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,26 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+
+_data
\ No newline at end of file
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..dbfe4b8
--- /dev/null
+++ b/index.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+ sketch 265
+
+
+
+
+
+
+
+ Github
+
+
+
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..fe114cb
--- /dev/null
+++ b/package.json
@@ -0,0 +1,30 @@
+{
+ "name": "sketch265",
+ "private": true,
+ "version": "0.0.0",
+ "scripts": {
+ "dev": "vite",
+ "build": "tsc && vite build",
+ "preview": "vite preview"
+ },
+ "devDependencies": {
+ "@types/events": "^3.0.0",
+ "@types/matter-js": "^0.17.7",
+ "@types/node": "^17.0.21",
+ "@types/three": "^0.141.0",
+ "autoprefixer": "^10.4.2",
+ "matter-js": "^0.18.0",
+ "postcss": "^8.4.6",
+ "snd-lib": "^1.0.1",
+ "three": "^0.141.0",
+ "typescript": "^4.5.4",
+ "vite": "^2.8.0",
+ "vite-plugin-glsl": "^0.0.8"
+ },
+ "dependencies": {
+ "current-device": "^0.10.2",
+ "gsap": "^3.9.1",
+ "lil-gui": "^0.16.0",
+ "stats.js": "^0.17.0"
+ }
+}
diff --git a/postcss.config.js b/postcss.config.js
new file mode 100644
index 0000000..de5c67a
--- /dev/null
+++ b/postcss.config.js
@@ -0,0 +1,5 @@
+module.exports = {
+ plugins: [
+ require('autoprefixer')()
+ ]
+}
\ No newline at end of file
diff --git a/src/core/conf.ts b/src/core/conf.ts
new file mode 100755
index 0000000..c24cb36
--- /dev/null
+++ b/src/core/conf.ts
@@ -0,0 +1,53 @@
+import { Util } from '../libs/util';
+
+export class Conf {
+ private static _instance: Conf;
+
+ // パラメータ
+ public FLG_PARAM: boolean = location.href.includes('p=yes');
+
+ // Stats
+ public FLG_STATS: boolean = location.href.includes('p=yes');
+
+ // パス
+ public PATH_IMG: string = './assets/img/';
+
+ // タッチデバイス
+ public USE_TOUCH: boolean = Util.instance.isTouchDevice();
+
+ // ブレイクポイント
+ public BREAKPOINT: number = 768;
+
+ // PSDサイズ
+ public LG_PSD_WIDTH: number = 1600;
+ public XS_PSD_WIDTH: number = 750;
+
+ // 簡易版
+ public IS_SIMPLE: boolean = Util.instance.isPc() && Util.instance.isSafari();
+
+ // スマホ
+ public IS_PC: boolean = Util.instance.isPc();
+ public IS_SP: boolean = Util.instance.isSp();
+ public IS_AND: boolean = Util.instance.isAod();
+ public IS_TAB: boolean = Util.instance.isIPad();
+ public USE_ROLLOVER:boolean = Util.instance.isPc() && !Util.instance.isIPad()
+
+ public ITEM_NUM:number = 20;
+ public STACK_NUM:number = 1;
+ public ITEM_SIZE:number = 2;
+ public COLOR_LIST:Array = [
+ 0x051181,
+ 0xfb3c3c,
+ 0xfeb64a,
+ 0x128b8e
+ ];
+
+
+ constructor() {}
+ public static get instance(): Conf {
+ if (!this._instance) {
+ this._instance = new Conf();
+ }
+ return this._instance;
+ }
+}
diff --git a/src/core/fps.ts b/src/core/fps.ts
new file mode 100755
index 0000000..cde062f
--- /dev/null
+++ b/src/core/fps.ts
@@ -0,0 +1,7 @@
+export class FPS {
+ static HIGH: number = 0;
+ static MIDDLE: number = 1;
+ static LOW: number = 2;
+
+ constructor() {}
+}
diff --git a/src/core/func.ts b/src/core/func.ts
new file mode 100755
index 0000000..8e24fa8
--- /dev/null
+++ b/src/core/func.ts
@@ -0,0 +1,86 @@
+import { ScreenType } from './screenType';
+import { Conf } from './conf';
+
+export class Func {
+ private static _instance: Func;
+ private _useFullScreen: boolean = false;
+
+ constructor() {}
+
+ public static get instance(): Func {
+ if (!this._instance) {
+ this._instance = new Func();
+ }
+ return this._instance;
+ }
+
+ public ratio(): number {
+ return window.devicePixelRatio || 1;
+ }
+
+ public px(num: number): string {
+ return num + 'px';
+ }
+
+ public useScreen(): boolean {
+ return screen != undefined;
+ }
+
+ public sw(): number {
+ return window.innerWidth;
+ }
+
+ public sh(): number {
+ if (this._useFullScreen) {
+ return screen.height;
+ } else {
+ return window.innerHeight;
+ }
+ }
+
+ public screenOffsetY(): number {
+ return (window.innerHeight - this.sh()) * 0.5;
+ }
+
+ public screen(): number {
+ if (window.innerWidth <= Conf.instance.BREAKPOINT) {
+ return ScreenType.XS;
+ } else {
+ return ScreenType.LG;
+ }
+ }
+
+ public isXS(): boolean {
+ return this.screen() == ScreenType.XS;
+ }
+
+ public isLG(): boolean {
+ return this.screen() == ScreenType.LG;
+ }
+
+ public val(xs: any, lg: any): any {
+ if (this.isXS()) {
+ return xs;
+ } else {
+ return lg;
+ }
+ }
+
+ public r(val: number): number {
+ const base = this.val(Conf.instance.XS_PSD_WIDTH, Conf.instance.LG_PSD_WIDTH);
+ return (val / base) * this.sw();
+ }
+
+ public sin1(radian:number):number {
+ return Math.sin(radian) + Math.sin(2 * radian)
+ }
+
+ public sin2(radian:number):number {
+ return (
+ Math.sin(radian) +
+ Math.sin(2.2 * radian + 5.52) +
+ Math.sin(2.9 * radian + 0.93) +
+ Math.sin(4.6 * radian + 8.94)
+ ) / 4
+ }
+}
diff --git a/src/core/mouse.ts b/src/core/mouse.ts
new file mode 100755
index 0000000..84a4575
--- /dev/null
+++ b/src/core/mouse.ts
@@ -0,0 +1,179 @@
+import { Point } from "../libs/point";
+import { Update } from "../libs/update";
+import { Util } from "../libs/util";
+
+
+export class Mouse {
+
+ private static _instance:Mouse;
+
+ public x:number = window.innerWidth * 0.5
+ public y:number = window.innerHeight * 0.5
+ public old:Point = new Point()
+ public normal:Point = new Point()
+ public easeNormal:Point = new Point()
+ public start:Point = new Point()
+ public moveDist:Point = new Point()
+ public dist:number = 0;
+ public isDown:boolean = false
+ public usePreventDefault:boolean = true
+
+ public onSwipe:any
+
+ private _updateHandler:any
+
+ constructor() {
+
+ if(Util.instance.isTouchDevice()) {
+ const tg = document.querySelector('.matter') || window
+ tg.addEventListener('touchstart', (e:any = {}) => {
+ this._eTouchStart(e)
+ }, {passive:false})
+ tg.addEventListener('touchend', () => {
+ this._eTouchEnd()
+ }, {passive:false})
+ tg.addEventListener('touchmove', (e:any = {}) => {
+ this._eTouchMove(e)
+ }, {passive:false})
+ } else {
+ window.addEventListener('mousedown', (e:any = {}) => {
+ this._eDown(e)
+ })
+ window.addEventListener('mouseup', () => {
+ this._eUp()
+ })
+ window.addEventListener('mousemove', (e:any = {}) => {
+ this._eMove(e)
+ })
+ // document.addEventListener('wheel', (e) => {
+ // if(this.usePreventDefault) {
+ // e.preventDefault()
+ // e.stopPropagation()
+ // }
+ // const test = Math.abs(e.deltaY)
+ // if(test > 5 && this._useWheel) {
+ // if(this.onSwipe != undefined) this.onSwipe({move:e.deltaY})
+ // this._useWheel = false
+ // setTimeout(() => {
+ // this._useWheel = true
+ // }, 1000)
+ // }
+ // }, {passive:false})
+ }
+
+ this._updateHandler = this._update.bind(this)
+ Update.instance.add(this._updateHandler)
+ }
+
+
+ public static get instance():Mouse {
+ if (!this._instance) {
+ this._instance = new Mouse();
+ }
+ return this._instance;
+ }
+
+
+ private _eTouchStart(e:any = {}):void {
+ this.isDown = true
+ this._eTouchMove(e)
+
+ const p:Point = this._getTouchPoint(e)
+ this.start.x = p.x
+ this.start.y = p.y
+ }
+
+
+ private _eTouchEnd():void {
+ this.isDown = false
+
+ // 上下スワイプ判定
+ const dx = this.old.x - this.x
+ const dy = this.old.y - this.y
+ // console.log(Math.abs(dy))
+ if(Math.abs(dy) > 0 || Math.abs(dx) > 0) {
+ if(this.onSwipe != undefined) this.onSwipe({move:dy})
+ }
+
+ this.dist = 0;
+ // console.log(dy)
+ // Param.instance.setMemo(dx + ',' + dy)
+ }
+
+
+ private _eTouchMove(e:any = {}):void {
+ const p:Point = this._getTouchPoint(e)
+ this.old.x = this.x
+ this.old.y = this.y
+ this.x = p.x
+ this.y = p.y
+
+ const dx = this.old.x - this.x
+ const dy = this.old.y - this.y
+ this.dist = Math.sqrt(dx * dx + dy * dy);
+
+ if(this.usePreventDefault) {
+ e.preventDefault()
+ }
+ }
+
+
+ private _eDown(e:any = {}):void {
+ this.isDown = true
+ this._eMove(e)
+
+ this.start.x = this.x
+ this.start.y = this.y
+ }
+
+
+ private _eUp():void {
+ this.isDown = false
+ }
+
+
+ private _eMove(e:any = {}):void {
+ this.old.x = this.x
+ this.old.y = this.y
+
+ this.x = e.clientX
+ this.y = e.clientY
+
+ const dx = this.old.x - this.x
+ const dy = this.old.y - this.y
+ this.dist = Math.sqrt(dx * dx + dy * dy);
+ }
+
+
+ private _getTouchPoint(e:TouchEvent):Point {
+ const p = new Point()
+ const touches:TouchList = e.touches
+ if(touches != null && touches.length > 0) {
+ p.x = touches[0].pageX
+ p.y = touches[0].pageY
+ }
+ return p
+ }
+
+
+ private _update():void {
+ if(this.isDown) {
+ this.moveDist.x = this.start.x - this.x
+ this.moveDist.y = this.start.y - this.y
+ } else {
+ this.moveDist.x += (0 - this.moveDist.x) * 0.25
+ this.moveDist.y += (0 - this.moveDist.y) * 0.25
+ }
+
+ this.normal.x = Util.instance.map(this.x, -1, 1, 0, window.innerWidth)
+ this.normal.y = Util.instance.map(this.y, -1, 1, 0, window.innerHeight)
+
+ const ease = 0.1
+ this.easeNormal.x += (this.normal.x - this.easeNormal.x) * ease
+ this.easeNormal.y += (this.normal.y - this.easeNormal.y) * ease
+
+ }
+
+
+
+}
\ No newline at end of file
diff --git a/src/core/myDisplay.ts b/src/core/myDisplay.ts
new file mode 100755
index 0000000..803602a
--- /dev/null
+++ b/src/core/myDisplay.ts
@@ -0,0 +1,141 @@
+import { Display } from '../libs/display';
+import { Update } from '../libs/update';
+import { Resize } from '../libs/resize';
+import { Point } from '../libs/point';
+
+export class MyDisplay extends Display {
+ private _updateHandler: any;
+ private _resizeHandler: any;
+
+ protected _c:number = 0;
+ protected _isEnter:boolean = false
+ protected _isOneEnter:boolean = false
+ protected _observer: any;
+ protected _elPos:Point = new Point(0, 9999);
+ protected _eRollOverHandler: any;
+ protected _eRollOutHandler: any;
+
+ constructor(opt:any = {}) {
+ super(opt);
+
+ if (opt.isDefEvent == undefined || opt.isDefEvent) {
+ this._updateHandler = this._update.bind(this);
+ Update.instance.add(this._updateHandler);
+
+ this._resizeHandler = this._resize.bind(this);
+ Resize.instance.add(this._resizeHandler);
+ }
+ }
+
+ init() {
+ super.init();
+ }
+
+
+ //
+ protected _setHover() {
+ this._eRollOverHandler = this._eRollOver.bind(this);
+ this._eRollOutHandler = this._eRollOut.bind(this);
+ this.getEl().addEventListener('mouseenter', this._eRollOverHandler);
+ this.getEl().addEventListener('mouseleave', this._eRollOutHandler);
+ }
+
+
+ //
+ protected _disposeHover() {
+ if(this._eRollOverHandler != null) {
+ this.getEl().removeEventListener('mouseenter', this._eRollOverHandler);
+ this.getEl().removeEventListener('mouseleave', this._eRollOutHandler);
+ this._eRollOverHandler = null;
+ this._eRollOutHandler = null;
+ }
+ }
+
+
+ //
+ protected _eRollOver() {
+ }
+
+
+ //
+ protected _eRollOut() {
+ }
+
+
+ //
+ protected _setObserver() {
+ this._observer = new IntersectionObserver((e) => {
+ if(e != undefined) {
+ e.forEach((val) => {
+ if (val != undefined && val.intersectionRatio > 0) {
+ this._eEnter()
+ } else {
+ this._eLeave()
+ }
+ })
+ }
+ },
+ {
+ root: null,
+ }
+ );
+
+ setTimeout(() => {
+ if(this._observer != undefined && this._observer != null) {
+ const tg = this.getEl()
+ if (tg != undefined) this._observer.observe(tg)
+ }
+ }, 100)
+ }
+
+
+ //
+ protected _eEnter() {
+ this._isEnter = true
+ }
+
+
+ //
+ protected _eLeave() {
+ this._isEnter = false
+ }
+
+
+ protected _disposeObserver() {
+ if (this._observer != null || this._observer != undefined) {
+ this._observer.unobserve(this.getEl());
+ this._observer = null;
+ }
+ }
+
+ // 破棄
+ public dispose() {
+ if(this._updateHandler != undefined) {
+ Update.instance.remove(this._updateHandler);
+ this._updateHandler = null;
+ }
+
+ if(this._resizeHandler != undefined) {
+ Resize.instance.remove(this._resizeHandler);
+ this._resizeHandler = null;
+ }
+
+ this._disposeHover();
+ this._disposeObserver();
+
+ super.dispose();
+ }
+
+ css(el: any, obj: any): void {
+ const style = el.style;
+ for (var key in obj) {
+ style[key] = obj[key];
+ }
+ }
+
+ protected _update(): void {
+ this._c++
+ }
+
+ protected _resize(): void {}
+}
diff --git a/src/core/param.ts b/src/core/param.ts
new file mode 100755
index 0000000..dae210b
--- /dev/null
+++ b/src/core/param.ts
@@ -0,0 +1,77 @@
+import GUI from 'lil-gui';
+import Stats from 'three/examples/jsm/libs/stats.module';
+import { Conf } from './conf';
+import { Update } from '../libs/update';
+import { FPS } from '../core/fps';
+import { Color } from "three/src/math/Color";
+
+export class Param {
+ private static _instance: Param;
+
+ public fps: number = FPS.MIDDLE;
+ public colors:Array = [];
+ public debug:HTMLElement = document.querySelector('.l-debug') as HTMLElement;
+
+ public selectedNo:Array = [0,0];
+
+ private _dat: any;
+ private _stats: any;
+
+
+ public main = {
+ bg:{value:0x000000, type:'color'},
+ }
+
+ constructor() {
+ if (Conf.instance.FLG_PARAM) {
+ this.makeParamGUI();
+ }
+
+ if (Conf.instance.FLG_STATS) {
+ this._stats = Stats();
+ document.body.appendChild(this._stats.domElement);
+ }
+
+ Update.instance.add(() => {
+ this._update();
+ });
+ }
+
+ private _update(): void {
+ if (this._stats != undefined) {
+ this._stats.update();
+ }
+ }
+
+ public static get instance(): Param {
+ if (!this._instance) {
+ this._instance = new Param();
+ }
+ return this._instance;
+ }
+
+ public makeParamGUI(): void {
+ if (this._dat != undefined) return;
+
+ this._dat = new GUI();
+ this._add(this.main, 'main');
+ }
+
+ private _add(obj: any, folderName: string): void {
+ const folder = this._dat.addFolder(folderName);
+ for (var key in obj) {
+ const val: any = obj[key];
+ if (val.use == undefined) {
+ if (val.type == 'color') {
+ folder.addColor(val, 'value').name(key);
+ } else {
+ if (val.list != undefined) {
+ folder.add(val, 'value', val.list).name(key);
+ } else {
+ folder.add(val, 'value', val.min, val.max).name(key);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/core/screenType.ts b/src/core/screenType.ts
new file mode 100755
index 0000000..81818cc
--- /dev/null
+++ b/src/core/screenType.ts
@@ -0,0 +1,6 @@
+export class ScreenType {
+ static LG: number = 0;
+ static XS: number = 1;
+
+ constructor() {}
+}
diff --git a/src/core/scroller.ts b/src/core/scroller.ts
new file mode 100755
index 0000000..b5601db
--- /dev/null
+++ b/src/core/scroller.ts
@@ -0,0 +1,43 @@
+import { Point } from '../libs/point';
+import { Update } from '../libs/update';
+
+export class Scroller {
+ private static _instance: Scroller;
+
+ public old: Point = new Point();
+ public val: Point = new Point();
+ public easeVal: Point = new Point();
+ public rate: Point = new Point();
+ public power: Point = new Point();
+ public dist: Point = new Point();
+
+ private _updateHandler: any;
+
+ public static get instance(): Scroller {
+ if (!this._instance) {
+ this._instance = new Scroller();
+ }
+ return this._instance;
+ }
+
+ constructor() {
+ this._updateHandler = this._update.bind(this);
+ Update.instance.add(this._updateHandler);
+ }
+
+ public set(val: number): void {
+ window.scrollTo(0, val);
+ }
+
+ private _update(): void {
+ this.old.copy(this.val);
+ this.val.y = Math.max(0, window.pageYOffset || document.documentElement.scrollTop);
+ this.easeVal.y += (this.val.y - this.easeVal.y) * 0.1
+
+ const ease = 0.1;
+ let powerTg = this.old.y - this.val.y;
+ this.power.y += (powerTg - this.power.y) * ease;
+
+ this.dist.y += (this.old.y - this.val.y - this.dist.y) * ease;
+ }
+}
diff --git a/src/core/tween.ts b/src/core/tween.ts
new file mode 100755
index 0000000..a4a7520
--- /dev/null
+++ b/src/core/tween.ts
@@ -0,0 +1,72 @@
+import { Power0, gsap } from 'gsap';
+
+export class Tween {
+ private static _instance: Tween;
+
+ constructor() {}
+
+ public static get instance(): Tween {
+ if (!this._instance) {
+ this._instance = new Tween();
+ }
+ return this._instance;
+ }
+
+ a(
+ target: any,
+ param: any,
+ duration: number = 1,
+ delay: number = 0,
+ easing: any = undefined,
+ onStart: any = undefined,
+ onUpdate: any = undefined,
+ onComplete: any = undefined
+ ): void {
+ gsap.killTweensOf(target);
+
+ let from:any = {};
+ let to:any = {};
+
+ for (var key in param) {
+ const val = param[key];
+ if (val[0] != undefined && val[0] != null) {
+ from[key] = val[0];
+ to[key] = val[1];
+ } else {
+ to[key] = val;
+ }
+ }
+
+ gsap.set(target, from);
+
+ if (easing == undefined) {
+ easing = Power0.easeNone;
+ }
+ to['ease'] = easing;
+
+ to['duration'] = duration;
+ to['delay'] = delay;
+
+ if (onStart != undefined) {
+ to['onStart'] = onStart;
+ }
+
+ if (onUpdate != undefined) {
+ to['onUpdate'] = onUpdate;
+ }
+
+ if (onComplete != undefined) {
+ to['onComplete'] = onComplete;
+ }
+
+ gsap.to(target, to);
+ }
+
+ set(target: any, to: any): void {
+ gsap.set(target, to);
+ }
+
+ kill(target: any): void {
+ gsap.killTweensOf(target);
+ }
+}
diff --git a/src/libs/display.ts b/src/libs/display.ts
new file mode 100755
index 0000000..85883a3
--- /dev/null
+++ b/src/libs/display.ts
@@ -0,0 +1,126 @@
+export class Display {
+ opt: any;
+ el: any;
+ constructor(opt: any = {}) {
+ this.opt = opt;
+ this.el = this.opt.el;
+ }
+
+ init() {}
+
+ // 破棄
+ public dispose() {
+ this.opt = null;
+ this.el = null;
+ }
+
+ public getEl(): HTMLElement {
+ return this.el as HTMLElement;
+ }
+
+ public hasData(name: string): boolean {
+ const v = this.getEl().getAttribute(name);
+ if (v == undefined) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ public getData(name: string, def: any): any {
+ const v = this.getEl().getAttribute(name);
+ if (v == undefined) {
+ return def;
+ } else {
+ return v;
+ }
+ }
+
+ public qs(sel: string): HTMLElement {
+ return this.el.querySelector(sel);
+ }
+
+ public qsAll(sel: string): Array {
+ return this.el.querySelectorAll(sel);
+ }
+
+ public hasClass(c: string): boolean {
+ return (this.el as HTMLElement).classList.contains(c);
+ }
+
+ public addClass(c: string): void {
+ (this.el as HTMLElement).classList.add(c);
+ }
+
+ public attachClass(el:any, c: string): void {
+ if(el != undefined) el.classList.add(c);
+ }
+
+ public detachClass(el:any, c: string): void {
+ if(el != undefined) el.classList.remove(c);
+ }
+
+ public removeClass(c: string): void {
+ (this.el as HTMLElement).classList.remove(c);
+ }
+
+ getWidth(el: Element): number {
+ let val = document.defaultView?.getComputedStyle(el, null).width;
+ return Number(val?.replace('px', ''));
+ }
+
+ getHeight(el: Element | null): number {
+ if(el == null) {
+ return 0
+ } else {
+ let val = document.defaultView?.getComputedStyle(el, null).height;
+ return Number(val?.replace('px', ''));
+ }
+ }
+
+ getRect(el: Element): any {
+ const st = document.defaultView?.getComputedStyle(el, null);
+ if (st != undefined) {
+ return {
+ width: Number(st.width.replace('px', '')),
+ height: Number(st.height.replace('px', '')),
+ };
+ } else {
+ return {};
+ }
+ }
+
+ public getDataNumber(name: string): number {
+ const d = this.getEl().getAttribute(name);
+ if (d == undefined) {
+ return 0;
+ } else {
+ return Number(d);
+ }
+ }
+
+ public getOffsetTop(el: Element): number {
+ const rect = el.getBoundingClientRect();
+ var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
+ return rect.top + scrollTop;
+ }
+
+ public getOffset(el: Element): any {
+ const rect = el.getBoundingClientRect();
+ var scrollTop = window.pageYOffset || document.documentElement.scrollTop;
+ return {
+ y: rect.top + scrollTop,
+ x: rect.left,
+ };
+ }
+
+ protected _call(f:any, arg:any = null):void {
+ if(f != undefined) {
+ if(arg != null) {
+ f(arg)
+ } else {
+ f()
+ }
+ }
+ }
+}
diff --git a/src/libs/easing.ts b/src/libs/easing.ts
new file mode 100755
index 0000000..e490c11
--- /dev/null
+++ b/src/libs/easing.ts
@@ -0,0 +1,69 @@
+export class Easing {
+ private static _instance: Easing;
+
+ private constructor() {}
+
+ public static get instance(): Easing {
+ if (!this._instance) {
+ this._instance = new Easing();
+ }
+ return this._instance;
+ }
+
+ inExpo(t: number): number {
+ if(t == 0) {
+ return 0;
+ } else {
+ return Math.pow(2, 10 * (t - 1));
+ }
+ }
+
+
+ outExpo(t: number): number {
+ if(t == 1) {
+ return 1;
+ } else {
+ return -Math.pow(2, -10 * t) + 1;
+ }
+ }
+
+
+ outSine(t: number): number {
+ return Math.sin(t * (Math.PI / 2));
+ }
+
+
+ outQuad(t: number): number {
+ return -t * (t - 2);
+ }
+
+
+ inOutQuart(t: number): number {
+ t *= 2;
+ if (t < 1) return 0.5 * t * t * t * t;
+ t -= 2;
+ return -0.5 * (t * t * t * t - 2);
+ }
+
+ inOutCirc(t: number): number {
+ t *= 2;
+
+ if (t < 1) return -0.5 * (Math.sqrt(1 - t * t) - 1);
+
+ t -= 2;
+ return 0.5 * (Math.sqrt(1 - t * t) + 1);
+ }
+
+ inOutCubic(t: number): number {
+ t *= 2;
+
+ if (t < 1) return 0.5 * t * t * t;
+
+ t -= 2;
+ return 0.5 * (t * t * t + 2);
+ }
+
+ inOutSine(t: number): number {
+ return -0.5 * (Math.cos(Math.PI * t) - 1);
+ }
+}
diff --git a/src/libs/hsl.ts b/src/libs/hsl.ts
new file mode 100755
index 0000000..8253a70
--- /dev/null
+++ b/src/libs/hsl.ts
@@ -0,0 +1,10 @@
+
+export class HSL {
+
+ h:number = 0
+ s:number = 0
+ l:number = 0
+
+ constructor() {
+ }
+}
\ No newline at end of file
diff --git a/src/libs/point.ts b/src/libs/point.ts
new file mode 100755
index 0000000..f6b5f1a
--- /dev/null
+++ b/src/libs/point.ts
@@ -0,0 +1,19 @@
+export class Point {
+ x: number = 0;
+ y: number = 0;
+
+ constructor(x: number = 0, y: number = 0) {
+ this.x = x;
+ this.y = y;
+ }
+
+ public set(x: number = 0, y: number = 0) {
+ this.x = x;
+ this.y = y;
+ }
+
+ public copy(p: Point) {
+ this.x = p.x;
+ this.y = p.y;
+ }
+}
diff --git a/src/libs/rect.ts b/src/libs/rect.ts
new file mode 100755
index 0000000..bb2c397
--- /dev/null
+++ b/src/libs/rect.ts
@@ -0,0 +1,13 @@
+export class Rect {
+ x: number = 0;
+ y: number = 0;
+ width: number = 0;
+ height: number = 0;
+
+ constructor(x: number = 0, y: number = 0, width: number = 0, height: number = 0) {
+ this.x = x;
+ this.y = y;
+ this.width = width;
+ this.height = height;
+ }
+}
diff --git a/src/libs/resize.ts b/src/libs/resize.ts
new file mode 100755
index 0000000..c468462
--- /dev/null
+++ b/src/libs/resize.ts
@@ -0,0 +1,70 @@
+import { Rect } from './rect';
+
+export class Resize {
+ private static _instance: Resize;
+
+ // レイアウト更新時に実行させる関数を保持
+ private _list: Array = [];
+
+ private _timer: any = null;
+
+ public size: Rect = new Rect();
+ public oldSize: Rect = new Rect();
+
+ constructor() {
+ window.addEventListener(
+ 'resize',
+ () => {
+ this._eResize();
+ },
+ false
+ );
+ }
+
+ public static get instance(): Resize {
+ if (!this._instance) {
+ this._instance = new Resize();
+ }
+ return this._instance;
+ }
+
+ private _eResize(): void {
+ this._setStageSize();
+
+ if (this._timer === null) {
+ clearInterval(this._timer);
+ this._timer = null;
+ }
+
+ this._timer = setTimeout(() => {
+ this._call();
+ this.oldSize.width = this.size.width;
+ this.oldSize.height = this.size.height;
+ }, 300);
+ }
+
+ private _setStageSize(): void {
+ this.size.width = window.innerWidth;
+ this.size.height = window.innerHeight;
+ }
+
+ public add(f: Function) {
+ this._list.push(f);
+ }
+
+ public remove(f: Function) {
+ const arr: Array = [];
+ this._list.forEach((val) => {
+ if (val != f) {
+ arr.push(val);
+ }
+ });
+ this._list = arr;
+ }
+
+ private _call = () => {
+ for (var item of this._list) {
+ if (item != null) item();
+ }
+ };
+}
diff --git a/src/libs/update.ts b/src/libs/update.ts
new file mode 100755
index 0000000..01b4a96
--- /dev/null
+++ b/src/libs/update.ts
@@ -0,0 +1,46 @@
+export class Update {
+ private static _instance: Update;
+
+ // 更新回数
+ public cnt: number = 0;
+
+ // 毎フレーム実行させる関数を保持
+ private _updateList: Array = [];
+
+ public play: boolean = true;
+
+ constructor() {
+ window.requestAnimationFrame(this._update);
+ }
+
+ public static get instance(): Update {
+ if (!this._instance) {
+ this._instance = new Update();
+ }
+ return this._instance;
+ }
+
+ public add(f: Function) {
+ this._updateList.push(f);
+ }
+
+ public remove(f: Function) {
+ const arr: Array = [];
+ this._updateList.forEach((val) => {
+ if (val != f) {
+ arr.push(val);
+ }
+ });
+ this._updateList = arr;
+ }
+
+ _update = () => {
+ if (this.play) {
+ this.cnt++;
+ for (var item of this._updateList) {
+ if (item != null) item();
+ }
+ window.requestAnimationFrame(this._update);
+ }
+ };
+}
diff --git a/src/libs/util.ts b/src/libs/util.ts
new file mode 100755
index 0000000..3da72d8
--- /dev/null
+++ b/src/libs/util.ts
@@ -0,0 +1,286 @@
+import device from 'current-device';
+
+export class Util {
+ private static _instance: Util;
+
+ private constructor() {}
+
+ public static get instance(): Util {
+ if (!this._instance) {
+ this._instance = new Util();
+ }
+
+ // 生成済みのインスタンスを返す
+ return this._instance;
+ }
+
+ // ランダムな数(float)
+ // -----------------------------------
+ // @min : 最小値(float)
+ // @max : 最大値(float)
+ // return : min(含む)からmax(含む)までのランダムな数(float)
+ // -----------------------------------
+ random(min: number, max: number): number {
+ return Math.random() * (max - min) + min;
+ }
+
+ random2(min: number, max: number): number {
+ let r: number = Math.random() * (max - min) + min;
+ if (this.hit(2)) {
+ r *= -1;
+ }
+ return r;
+ }
+
+ // ランダムな数(int)
+ // -----------------------------------
+ // @min : 最小値(int)
+ // @max : 最大値(int)
+ // return : min(含む)からmax(含む)までのランダムな数(int)
+ // -----------------------------------
+ randomInt(min: number, max: number): number {
+ return Math.floor(Math.random() * (max - min + 1)) + min;
+ }
+
+ // 1/@rangeの確率でtrueを取得
+ // -----------------------------------
+ // @range : 2以上の分母(int)
+ // return : true or false(boolean)
+ // -----------------------------------
+ hit(range: number = 0): boolean {
+ if (range < 2) range = 2;
+ return this.randomInt(0, range - 1) == 0;
+ }
+
+ // 配列内の値をランダムに取得
+ // -----------------------------------
+ randomArr(arr: Array): any {
+ return arr[this.randomInt(0, arr.length - 1)];
+ }
+
+ // -指定値から指定値までのランダムな数(float)
+ // -----------------------------------
+ // @val : 指定値(float)
+ // return : -@valから@valまでのランダムな数(float)
+ // -----------------------------------
+ range(val: number): number {
+ return this.random(-val, val);
+ }
+
+ // 値を範囲内におさめる
+ // -----------------------------------
+ // @val : 値
+ // @min : 最小値
+ // @max : 最大値
+ // -----------------------------------
+ clamp(val: number, min: number, max: number): number {
+ return Math.min(max, Math.max(val, min));
+ }
+
+ // 値のマッピング
+ // -----------------------------------
+ // @num : マッピングする値
+ // @toMin : 変換後の最小値
+ // @toMax : 変換後の最大値
+ // @fromMin : 変換前の最小値
+ // @fromMax : 変換前の最大値
+ // -----------------------------------
+ map(num: number, toMin: number, toMax: number, fromMin: number, fromMax: number): number {
+ if (num <= fromMin) return toMin;
+ if (num >= fromMax) return toMax;
+
+ const p = (toMax - toMin) / (fromMax - fromMin);
+ return (num - fromMin) * p + toMin;
+ }
+
+ // 線形補完
+ // -----------------------------------
+ mix(x: number, y: number, a: number): number {
+ return x * (1 - a) + y * a;
+ }
+
+ // ラジアンに変換
+ // -----------------------------------
+ radian(degree: number): number {
+ return (degree * Math.PI) / 180;
+ }
+
+ // 角度に変換
+ // -----------------------------------
+ degree(radian: number): number {
+ return (radian * 180) / Math.PI;
+ }
+
+ // 配列をランダムに並べ替え
+ // -----------------------------------
+ shuffle(arr: Array): void {
+ let i = arr.length;
+ while (--i) {
+ let j = Math.floor(Math.random() * (i + 1));
+ if (i == j) continue;
+ let k = arr[i];
+ arr[i] = arr[j];
+ arr[j] = k;
+ }
+ }
+
+ // 文字列の全置換
+ // -----------------------------------
+ replaceAll(val: string, org: string, dest: string): string {
+ return val.split(org).join(dest);
+ }
+
+ // 配列内のパラメータを比較してソート
+ // -----------------------------------
+ // @arr : 配列
+ // @para : パラメーター名
+ // @desc : 降順かどうか(boolean) デフォルトは昇順
+ // -----------------------------------
+ sort(arr: Array, para: string, desc: boolean = true): void {
+ if (desc) {
+ arr.sort((a: any, b: any) => {
+ return b[para] - a[para];
+ });
+ } else {
+ arr.sort((a: any, b: any) => {
+ return a[para] - b[para];
+ });
+ }
+ }
+
+ // 2点間の距離
+ // -----------------------------------
+ distance(x1: number, y1: number, x2: number, y2: number): number {
+ const dx = x1 - x2;
+ const dy = y1 - y2;
+ return Math.sqrt(dx * dx + dy * dy);
+ }
+
+ // 数値を文字列に変換
+ // -----------------------------------
+ // @num : 数値
+ // @keta : 桁数
+ // -----------------------------------
+ numStr(num: number, keta: number): string {
+ let str = String(num);
+ if (str.length >= keta) return str;
+
+ const len = keta - str.length;
+ let i = 0;
+ while (i < len) {
+ str = '0' + str;
+ i++;
+ }
+
+ return str;
+ }
+
+ // IEかどうか Edge含む
+ // -----------------------------------
+ isIE(): boolean {
+ const ua = window.navigator.userAgent.toLowerCase();
+ return ua.indexOf('msie') != -1 || ua.indexOf('trident/7') != -1 || ua.indexOf('edge') != -1;
+ }
+
+ // IEかどうか Edge含まない
+ // -----------------------------------
+ isIE2(): boolean {
+ const ua = window.navigator.userAgent.toLowerCase();
+ return ua.indexOf('msie') != -1 || ua.indexOf('trident/7') != -1;
+ }
+
+ // WINかどうか
+ // -----------------------------------
+ isWin(): boolean {
+ return window.navigator.platform.indexOf('Win') != -1;
+ }
+
+ // googleChromeかどうか
+ // -----------------------------------
+ isChrome(): boolean {
+ return window.navigator.userAgent.toLowerCase().indexOf('chrome') != -1;
+ }
+
+ // FireFoxかどうか
+ // -----------------------------------
+ isFF(): boolean {
+ return window.navigator.userAgent.toLowerCase().indexOf('firefox') != -1;
+ }
+
+ // Safariかどうか
+ // -----------------------------------
+ isSafari(): boolean {
+ return window.navigator.userAgent.toLowerCase().indexOf('safari') != -1 && !this.isChrome();
+ }
+
+ // -----------------------------------
+ // webGL使えるか
+ // -----------------------------------
+ useWebGL(): boolean {
+ try {
+ const c = document.createElement('canvas');
+ const w: any = c.getContext('webgl') || c.getContext('experimental-webgl');
+ return !!(window.WebGLRenderingContext && w && w.getShaderPrecisionFormat);
+ } catch (e) {
+ return false;
+ }
+ }
+
+ // クエリ抜き出し
+ // -----------------------------------
+ // @key : 抜き出すパラメータ名(String)
+ // -----------------------------------
+ getQuery(key: string): string {
+ key = key.replace(/[€[]/, '€€€[').replace(/[€]]/, '€€€]');
+ const regex = new RegExp('[€€?&]' + key + '=([^&//]*)');
+ const qs = regex.exec(window.location.href);
+ if (qs == null) {
+ return '';
+ } else {
+ return qs[1];
+ }
+ }
+
+ // -----------------------------------
+ // タッチデバイスかどうか
+ // -----------------------------------
+ isTouchDevice(): boolean {
+ const isTouch = !!('ontouchstart' in window || (navigator != undefined && navigator.maxTouchPoints > 0));
+ return isTouch;
+ }
+
+ // -----------------------------------
+ // PCかどうか
+ // -----------------------------------
+ isPc(): boolean {
+ return (device.mobile() == false)
+ }
+
+ // -----------------------------------
+ // スマホかどうか
+ // -----------------------------------
+ isSp(): boolean {
+ return device.mobile()
+ }
+
+ // -----------------------------------
+ // Androidかどうか
+ // -----------------------------------
+ isAod(): boolean {
+ return device.android();
+ }
+
+ // -----------------------------------
+ // iPhoneかどうか
+ // -----------------------------------
+ isIPhone(): boolean {
+ return device.iphone();
+ }
+
+ // -----------------------------------
+ // iPadかどうか
+ // -----------------------------------
+ isIPad(): boolean {
+ return device.tablet();
+ }
+}
diff --git a/src/libs/val.ts b/src/libs/val.ts
new file mode 100755
index 0000000..f9aa977
--- /dev/null
+++ b/src/libs/val.ts
@@ -0,0 +1,7 @@
+export class Val {
+ val: number = 0;
+
+ constructor(val: number = 0) {
+ this.val = val;
+ }
+}
diff --git a/src/main.ts b/src/main.ts
new file mode 100644
index 0000000..3eacffb
--- /dev/null
+++ b/src/main.ts
@@ -0,0 +1,8 @@
+import './style.css'
+import { Contents } from './parts/contents';
+
+new Contents({
+ el:document.querySelector('.l-main'),
+})
+
+
diff --git a/src/parts/contents.ts b/src/parts/contents.ts
new file mode 100755
index 0000000..2dbbd6e
--- /dev/null
+++ b/src/parts/contents.ts
@@ -0,0 +1,72 @@
+
+import { Func } from "../core/func";
+import { MyDisplay } from "../core/myDisplay";
+import { Point } from "../libs/point";
+import { Util } from "../libs/util";
+import { Text } from "./text";
+import { Visual } from "./visual";
+
+// -----------------------------------------
+//
+// -----------------------------------------
+export class Contents extends MyDisplay {
+
+ private _pos:Point = new Point();
+ private _text:Array = [];
+
+ private _v:Visual;
+
+ constructor(opt:any) {
+ super(opt)
+
+ this._v = new Visual({
+ el:this.getEl()
+ })
+
+ const text = '水の中で見えるテキスト';
+
+ let txt = '';
+ let arr = Array.from(text);
+ for(let i = 0; i < arr.length; i++) {
+ txt += '' + arr[i % arr.length] + '';
+ }
+ txt += '
';
+ (document.querySelector('.l-text') as HTMLElement).innerHTML = txt;
+ document.querySelectorAll('.l-text span').forEach((val) => {
+ const t = new Text({
+ el:val
+ });
+ this._text.push(t);
+ })
+
+ this._resize();
+ }
+
+
+ protected _update(): void {
+ super._update();
+
+ // モニターサイズと位置
+ const displayInfo: {x:number, y:number, width:number, height:number} = {
+ width:window.screen.width,
+ height:window.screen.height,
+ x:window.screenX,
+ y:window.screenY,
+ }
+
+ this._pos.y += (displayInfo.y - this._pos.y) * 0.1
+
+ // const sw = Func.instance.sw();
+ const sh = Func.instance.sh();
+
+ // 波の位置
+ const waveY = Util.instance.map(this._pos.y, -sh * 0.5, sh * 1, 0, displayInfo.height - sh);
+ this._v.baseY = waveY;
+
+ }
+
+
+ protected _resize(): void {
+ super._resize();
+ }
+}
\ No newline at end of file
diff --git a/src/parts/text.ts b/src/parts/text.ts
new file mode 100755
index 0000000..9b45c41
--- /dev/null
+++ b/src/parts/text.ts
@@ -0,0 +1,36 @@
+import { MyDisplay } from "../core/myDisplay";
+import { Tween } from "../core/tween";
+import { Util } from "../libs/util";
+
+// -----------------------------------------
+//
+// -----------------------------------------
+export class Text extends MyDisplay {
+
+ // private _noise:number = Util.instance.random(0, 1)
+
+ constructor(opt:any) {
+ super(opt)
+
+ this._c = Util.instance.random(0, 10000)
+
+ this._resize();
+ }
+
+
+ protected _update(): void {
+ super._update();
+
+ const radian = Util.instance.radian(this._c * 1);
+ Tween.instance.set(this.getEl(), {
+ rotationZ:Math.sin(radian) * 10,
+ y:Math.cos(radian * 1.2) * 10,
+ x:Math.sin(radian * -0.95) * 2,
+ })
+ }
+
+
+ protected _resize(): void {
+ super._resize();
+ }
+}
\ No newline at end of file
diff --git a/src/parts/visual.ts b/src/parts/visual.ts
new file mode 100755
index 0000000..5291103
--- /dev/null
+++ b/src/parts/visual.ts
@@ -0,0 +1,126 @@
+import { Func } from '../core/func';
+import { Canvas } from '../webgl/canvas';
+import { Object3D } from 'three/src/core/Object3D';
+import { MeshBasicMaterial } from "three/src/materials/MeshBasicMaterial";
+import { Mesh } from 'three/src/objects/Mesh';
+import { Color } from 'three/src/math/Color';
+import { Vector3 } from 'three/src/math/Vector3';
+import { CatmullRomCurve3 } from 'three/src/extras/curves/CatmullRomCurve3';
+import { Util } from "../libs/util";
+import { ShapeGeometry } from 'three/src/geometries/ShapeGeometry';
+import { Shape } from 'three/src/extras/core/Shape';
+
+export class Visual extends Canvas {
+
+ private _con:Object3D;
+ private _mesh:Mesh;
+
+ public baseY:number = 0;
+
+ constructor(opt: any) {
+ super(opt);
+
+ this._con = new Object3D();
+ this.mainScene.add(this._con);
+
+ this._mesh = new Mesh(
+ this._makeGeo(),
+ new MeshBasicMaterial({
+ color:0x0000ff,
+ }),
+ );
+ this._con.add(this._mesh);
+
+ this._resize();
+ }
+
+
+ protected _update(): void {
+ super._update();
+
+ this._mesh.geometry.dispose();
+ this._mesh.geometry = this._makeGeo();
+
+ if (this.isNowRenderFrame()) {
+ this._render()
+ }
+ }
+
+
+ private _render(): void {
+ const bgColor = new Color(0xffffff)
+ this.renderer.setClearColor(bgColor, 1)
+ this.renderer.render(this.mainScene, this.camera)
+ }
+
+
+ public isNowRenderFrame(): boolean {
+ return this.isRender
+ }
+
+
+ _resize(isRender: boolean = true): void {
+ super._resize();
+
+ const w = Func.instance.sw();
+ const h = Func.instance.sh();
+
+ this.renderSize.width = w;
+ this.renderSize.height = h;
+
+ this.updateCamera(this.camera, w, h);
+
+ let pixelRatio: number = window.devicePixelRatio || 1;
+
+ this.renderer.setPixelRatio(pixelRatio);
+ this.renderer.setSize(w, h);
+ this.renderer.clear();
+
+ if (isRender) {
+ this._render();
+ }
+ }
+
+
+ // ---------------------------------
+ //
+ // ---------------------------------
+ private _makeGeo():ShapeGeometry {
+ const arr:Array = []
+
+ const sw = Func.instance.sw();
+ const sh = Func.instance.sh();
+ const radius = sh * 0.1;
+
+ let startX = sw * -0.6;
+ let endX = sw * 0.6;
+ let startY = this.baseY;
+
+ let ang = 0;
+ let i = startX;
+ const interval = sw * 0.05;
+ while(i < endX) {
+ const radian = Util.instance.radian(this._c + ang);
+ arr.push(new Vector3(i, startY + Math.sin(radian) * radius, 0));
+ i += interval;
+ ang += 10;
+ }
+
+ arr.push(new Vector3(endX, -sh, 0));
+ arr.push(new Vector3(startX, -sh, 0));
+
+ const curve = new CatmullRomCurve3(arr, true);
+ const points = curve.getPoints(50);
+
+ const shape = new Shape()
+ points.forEach((val,i) => {
+ if(i == 0) {
+ shape.moveTo(val.x, val.y);
+ } else {
+ shape.lineTo(val.x, val.y)
+ }
+ });
+
+ return new ShapeGeometry(shape);
+ }
+}
diff --git a/src/style.css b/src/style.css
new file mode 100644
index 0000000..df27f29
--- /dev/null
+++ b/src/style.css
@@ -0,0 +1,50 @@
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ background-color: #000;
+ font-family: 'Noto Serif JP', serif;
+}
+
+.l-main {
+ position: absolute;
+ top: 0;
+ left: 0;
+}
+
+.l-text {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100vh;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ font-size: 48px;
+ color: #FFF;
+ letter-spacing: 0.5em;
+}
+
+.l-text span {
+ display: inline-block;
+}
+
+
+
+.link {
+ position: fixed;
+ right: 0;
+ bottom: 0;
+ padding: 10px;
+ font-size: 18px;
+ color: inherit;
+ text-decoration: none;
+ color: #FFF;
+ background-color: #000;
+ z-index: 9999;
+}
\ No newline at end of file
diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts
new file mode 100644
index 0000000..11f02fe
--- /dev/null
+++ b/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/src/webgl/canvas.ts b/src/webgl/canvas.ts
new file mode 100755
index 0000000..afbba38
--- /dev/null
+++ b/src/webgl/canvas.ts
@@ -0,0 +1,83 @@
+import { MyDisplay } from "../core/myDisplay";
+import { WebGLRenderer } from 'three/src/renderers/WebGLRenderer';
+import { Scene } from 'three/src/scenes/Scene';
+import { Rect } from "../libs/rect";
+import { OrthographicCamera } from 'three/src/cameras/OrthographicCamera';
+// import { PerspectiveCamera } from 'three/src/cameras/PerspectiveCamera';
+import { Camera } from 'three/src/cameras/Camera';
+import { Mesh } from 'three/src/objects/Mesh';
+import { Points } from 'three/src/objects/Points';
+import { Vector3 } from 'three/src/math/Vector3';
+
+export class Canvas extends MyDisplay {
+
+ public camera:Camera
+ public renderer:WebGLRenderer
+ public mainScene:Scene
+
+ public isRender:boolean = true
+ public renderSize:Rect = new Rect()
+
+ constructor(opt:any = {}) {
+ super(opt)
+
+ let renderParam:any = {
+ canvas : this.el,
+ antialias: false,
+ preserveDrawingBuffer : true,
+ powerPreference : 'low-power',
+ }
+ this.renderer = new WebGLRenderer(renderParam)
+ this.renderer.autoClear = true
+ this.renderer.setClearColor(0xffffff, 1)
+
+ this.mainScene = new Scene()
+
+ this.camera = this._makeCamera()
+ this.updateCamera(this.camera, 10, 10)
+ }
+
+ init() {
+ super.init()
+ }
+
+ protected _makeCamera():Camera {
+ return new OrthographicCamera(1, 1, 1, 1)
+ // return new PerspectiveCamera(50, 1, 0.0000001, 50000)
+ }
+
+ public updateCamera(camera:Camera, w:number = 10, h:number = 10) {
+ this._updateOrthCamera(camera as OrthographicCamera, w, h)
+ // this._updateOrthCamera(camera as PerspectiveCamera, w, h)
+ }
+
+ protected _updateOrthCamera(camera:OrthographicCamera, w:number = 10, h:number = 10) {
+ // protected _updateOrthCamera(camera:PerspectiveCamera, w:number = 10, h:number = 10) {
+ camera.left = -w * 0.5
+ camera.right = w * 0.5
+ camera.top = h * 0.5
+ camera.bottom = -h * 0.5
+ camera.near = -10000
+ camera.far = 100000
+ camera.updateProjectionMatrix()
+ camera.position.set(0, 0, 0)
+ camera.lookAt(new Vector3(0, 0, 0))
+
+ // camera.aspect = w / h
+ // camera.updateProjectionMatrix()
+ // camera.position.z = h / Math.tan(camera.fov * Math.PI / 360) / 2
+ }
+
+ protected _update():void {
+ super._update()
+ }
+
+ protected _setUni(m:Mesh | Points, name:string, val:any):void {
+ const uni = this._getUni(m)
+ uni[name].value = val
+ }
+
+ protected _getUni(mesh:any):any {
+ return mesh.material.uniforms
+ }
+}
\ No newline at end of file
diff --git a/src/webgl/ieTexture.ts b/src/webgl/ieTexture.ts
new file mode 100755
index 0000000..7f1b72a
--- /dev/null
+++ b/src/webgl/ieTexture.ts
@@ -0,0 +1,25 @@
+import { Texture } from 'three/src/textures/Texture';
+// import { RGBAFormat,RGBAFOr } from 'three/src/constants';
+
+export class IETexture {
+
+ constructor() {
+ }
+
+ // -----------------------------------
+ //
+ // -----------------------------------
+ public load(src:string, onLoaded:any):Texture {
+ const t = new Texture()
+ const img = new Image()
+ img.onload = () => {
+ t.image = img
+ // const isJPEG = (src.indexOf('.jpg') > 0)
+ // t.format = isJPEG ? RGBFormat : RGBAFormat
+ t.needsUpdate = true
+ if(onLoaded != undefined) onLoaded()
+ }
+ img.src = src
+ return t
+ }
+}
diff --git a/src/webgl/myObject3D.ts b/src/webgl/myObject3D.ts
new file mode 100755
index 0000000..2507d36
--- /dev/null
+++ b/src/webgl/myObject3D.ts
@@ -0,0 +1,59 @@
+import { Update } from "../libs/update";
+import { Resize } from "../libs/resize";
+import { Object3D } from 'three/src/core/Object3D';
+import { Mesh } from 'three/src/objects/Mesh';
+
+export class MyObject3D extends Object3D {
+
+ private _updateHandler:any
+ private _layoutHandler:any
+
+ protected _c:number = 0
+
+ constructor() {
+ super()
+
+ this._updateHandler = this._update.bind(this)
+ Update.instance.add(this._updateHandler)
+
+ this._layoutHandler = this._resize.bind(this)
+ Resize.instance.add(this._layoutHandler)
+ }
+
+ init() {
+ }
+
+ // 破棄
+ public dispose() {
+ Update.instance.remove(this._updateHandler)
+ this._updateHandler = null
+
+ Resize.instance.remove(this._layoutHandler)
+ this._layoutHandler = null
+ }
+
+
+ protected _update():void {
+ this._c++
+ }
+
+
+ protected _resize():void {
+ }
+
+
+ protected _getUni(mesh:any):any {
+ return mesh.material.uniforms
+ }
+
+
+ protected _setUni(m:Mesh, name:string, val:any):void {
+ const uni = this._getUni(m)
+ uni[name].value = val
+ }
+
+
+ protected _call(f:any):void {
+ if(f != undefined) f()
+ }
+}
\ No newline at end of file
diff --git a/src/webgl/texLoader.ts b/src/webgl/texLoader.ts
new file mode 100755
index 0000000..de7c103
--- /dev/null
+++ b/src/webgl/texLoader.ts
@@ -0,0 +1,151 @@
+import { TextureLoader } from 'three/src/loaders/TextureLoader';
+import { Texture } from 'three/src/textures/Texture';
+import { Util } from '../libs/util';
+import { IETexture } from './ieTexture';
+
+export class TexLoader {
+
+ private static _instance:TexLoader;
+
+ private _list:Array = []
+ private _tex:Array = []
+ // private _num:number = 0
+ private _loadedNum:number = 0
+ private _isIE:boolean = Util.instance.isIE()
+
+ public onComplete?:Function
+ public onProgress?:Function
+
+ constructor() {
+ }
+
+ public static get instance():TexLoader {
+ if (!this._instance) {
+ this._instance = new TexLoader();
+ }
+ return this._instance;
+ }
+
+
+ // -----------------------------------
+ //
+ // -----------------------------------
+ public load(opt:any):void {
+ this.onComplete = opt.onComplete
+ this.onProgress = opt.onProgress
+
+ if(opt.list.length == 0) {
+ if(this.onComplete != undefined) {
+ this.onComplete()
+ }
+ return
+ }
+
+ this._list = opt.list
+ this._loadedNum = 0
+
+ this._load()
+ }
+
+
+ // -----------------------------------
+ //
+ // -----------------------------------
+ private _load():void {
+ const data = this._list[this._loadedNum]
+
+ if(this.check(data.src)) {
+ this._loadedImg()
+ return
+ }
+
+ let texloader,tex
+ if(this._isIE) {
+ texloader = new IETexture()
+ tex = texloader.load(data.src, () => {
+ this._loadedImg()
+ })
+ } else {
+ texloader = new TextureLoader()
+ texloader.crossOrigin = '*'
+ tex = texloader.load(data.src, () => {
+ this._loadedImg()
+ })
+ }
+
+ this._tex.push({
+ tex:tex,
+ src:data.src
+ })
+ }
+
+
+ // -----------------------------------
+ //
+ // -----------------------------------
+ private _loadedImg():void {
+ this._loadedNum++
+
+ if(this.onProgress != undefined) {
+ this.onProgress(this._loadedNum / this._list.length)
+ }
+
+ if(this._loadedNum >= this._list.length) {
+ if(this.onComplete != undefined) {
+ this.onComplete()
+ }
+ return
+ }
+
+ this._load()
+ }
+
+
+ // -----------------------------------
+ //
+ // -----------------------------------
+ public get(src:string):Texture {
+
+ let preTex:any = null;
+ this._tex.forEach((item) => {
+ if(item.src == src) {
+ preTex = item.tex
+ }
+ })
+ if(preTex != null) {
+ return preTex
+ }
+
+ let t:any
+ if(!this._isIE) {
+ t = new TextureLoader()
+ t.crossOrigin = 'anonymous'
+ } else {
+ t = new IETexture()
+ }
+
+ const tex = t.load(src)
+
+ this._tex.push({
+ tex:tex,
+ src:src
+ })
+
+ return tex
+ }
+
+
+ // -----------------------------------
+ //
+ // -----------------------------------
+ public check(src:string):boolean {
+ let flg = false
+ this._tex.forEach((item) => {
+ if(item.src == src) {
+ flg = true
+ }
+ })
+ return flg
+ }
+
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..1885c8f
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,18 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "useDefineForClassFields": true,
+ "module": "ESNext",
+ "lib": ["ESNext", "DOM"],
+ "moduleResolution": "Node",
+ "strict": true,
+ "sourceMap": true,
+ "resolveJsonModule": true,
+ "esModuleInterop": true,
+ "noEmit": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noImplicitReturns": true
+ },
+ "include": ["src"]
+}
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..ff36c62
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,11 @@
+import glsl from 'vite-plugin-glsl'
+import { defineConfig } from 'vite'
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [glsl()],
+ base: './',
+ server: {
+ host: true
+ }
+})
\ No newline at end of file