diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..91c5032 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,3 @@ +###### 下記の問題を説明してください。 スクリーンショットと診断情報を含めてください。 +###### Describe the problem you are having below. Please include a screenshot and the diagnostic information. + diff --git a/public/src/css/main.css b/public/src/css/main.css index b91867a..c188c95 100644 --- a/public/src/css/main.css +++ b/public/src/css/main.css @@ -12,8 +12,10 @@ body{ width: 100%; height: 100%; background: #fe7839; + position: absolute; user-select: none; touch-action: none; + overflow: hidden; } #screen{ width: 100%; @@ -135,30 +137,34 @@ body{ #tutorial{ background: rgb(246, 234, 212); color: black; - border: 5px black solid; - border-radius: 10px; - height: 65%; - max-width: 800px; - padding: 20px; - margin: 8px; - font-size: 16pt; + border: 0.25em black solid; + border-radius: 0.5em; + width: 800px; + padding: 1em; + margin: 1em; + font-size: 21px; position: relative; } +.touch-enabled #tutorial{ + font-size: 3vmin; +} #tutorial-title{ z-index: 1; position: absolute; color: white; - top: -25px; - font-size: 26pt; + top: -0.7em; + font-size: 1.65em; } #tutorial-content{ - padding: 15px 30px; + margin: 0.7em 0; + overflow-y: auto; + max-height: calc(100vh - 14em); } kbd{ font-family: inherit; padding: 0.1em 0.6em; border: 1px solid #ccc; - font-size: 13px; + font-size: 0.6em; background-color: #f7f7f7; color: #333; box-shadow: 0 1px 0px rgba(0, 0, 0, 0.2), 0 0 0 2px #ffffff inset; @@ -169,33 +175,69 @@ kbd{ white-space: nowrap; } .taibtn{ - bottom: 15px; - margin: 0 auto; - position: absolute; - right: 15px; - padding: 10px 40px; - border-radius: 15px; - border: 3px rgba(218, 205, 178, 1) solid; + display: inline-block; + padding: 0.4em 0.4em; + border-radius: 0.5em; + border: 0.1em rgba(218, 205, 178, 1) solid; cursor: pointer; -} -.taibtn:hover{ - z-index: 1; - color: white; - background: rgb(255, 181, 71); - border-color: white; -} -.taibtn::before{ - padding: 0 40px; + font-size: 1.4em; + box-sizing: border-box; + color: #555; + text-align: center; } #tutorial-end-button{ - font-size: 22pt; + float: right; + padding: 0.4em 1.5em; + font-weight: bold; + border-color: #000; + color: #000; +} +.taibtn:hover, +#tutorial-end-button:hover{ + position: relative; + z-index: 1; + color: #fff; + background: #ffb547; + border-color: #fff; +} +.taibtn::before{ + padding-left: inherit; +} +#about-link-btns{ + float: left; + display: flex; +} +#about-link-btns .taibtn{ + margin-right: 0.4em; +} +#diag-txt textarea, +#diag-txt iframe{ + width: 100%; + height: 5em; + font-size: inherit; + resize: none; + word-break: break-all; + margin-bottom: 1em; + background: #fff; + border: 1px solid #a9a9a9; +} +.text-warn{ + color: #d00; +} +.link-btn a{ + color: inherit; + text-decoration: none; + pointer-events: none; +} +.nowrap{ + white-space: nowrap; } @keyframes bgscroll{ from{ - background-position: 0 center; + background-position: 0 top; } to{ - background-position: calc(-100vh / 720 * 512) center; + background-position: calc(-100vh / 720 * 512) top; } } #song-select{ @@ -210,9 +252,7 @@ kbd{ } #song-sel-canvas{ position: absolute; - top: 0; right: 0; - bottom: 0; left: 0; margin: auto; } diff --git a/public/src/js/about.js b/public/src/js/about.js new file mode 100644 index 0000000..a5e52c4 --- /dev/null +++ b/public/src/js/about.js @@ -0,0 +1,149 @@ +class About{ + constructor(touchEnabled){ + this.issueTemplate = [ + "###### 下記の問題を説明してください。 スクリーンショットと診断情報を含めてください。", + "###### Describe the problem you are having below. Please include a screenshot and the diagnostic information." + ] + + this.touchEnabled = touchEnabled + loader.changePage("about") + cancelTouch = false + + this.endButton = document.getElementById("tutorial-end-button") + this.diagTxt = document.getElementById("diag-txt") + this.version = document.getElementById("version-link").href + this.tutorialOuter = document.getElementById("tutorial-outer") + if(touchEnabled){ + this.tutorialOuter.classList.add("touch-enabled") + } + this.linkGithub = document.getElementById("link-github") + this.linkEmail = document.getElementById("link-email") + + pageEvents.add(this.linkGithub, ["click", "touchend"], this.linkButton.bind(this)) + pageEvents.add(this.linkEmail, ["click", "touchend"], this.linkButton.bind(this)) + pageEvents.once(this.endButton, ["mousedown", "touchstart"]).then(this.onEnd.bind(this)) + pageEvents.keyOnce(this, 13, "down").then(this.onEnd.bind(this)) + + this.gamepad = new Gamepad({ + "confirm": ["start", "b", "ls", "rs"] + }, this.onEnd.bind(this)) + + this.addDiag() + } + onEnd(event){ + var touched = false + if(event && event.type === "touchstart"){ + event.preventDefault() + touched = true + } + this.clean() + assets.sounds["don"].play() + localStorage.setItem("tutorial", "true") + setTimeout(() => { + new SongSelect("about", false, touched) + }, 500) + } + addDiag(){ + var diag = [] + + diag.push("```") + diag.push("Taiko-Web version: " + this.version) + diag.push("User agent: " + navigator.userAgent) + diag.push("Screen size: " + innerWidth + "x" + innerHeight + ", outer: " + outerWidth + "x" + outerHeight + ", ratio: " + (window.devicePixelRatio || 1).toFixed(2)) + if(this.touchEnabled){ + diag.push("Touch enabled: true") + } + if(!fullScreenSupported){ + diag.push("Full screen supported: false") + } + diag.push("Blur performance: " + perf.blur + "ms, all images: " + perf.allImg + "ms") + diag.push("Page load: " + (perf.load / 1000).toFixed(1) + "s") + if("getGamepads" in navigator){ + var gamepads = navigator.getGamepads() + for(var i = 0; i < gamepads.length; i++){ + if(gamepads[i]){ + var gamepadDiag = [] + gamepadDiag.push(gamepads[i].id) + gamepadDiag.push("buttons: " + gamepads[i].buttons.length) + gamepadDiag.push("axes: " + gamepads[i].axes.length) + diag.push("Gamepad #" + (i + 1) + ": " + gamepadDiag.join(", ")) + } + } + } + var errorObj = {} + if(localStorage["lastError"]){ + try{ + errorObj = JSON.parse(localStorage["lastError"]) + }catch(e){} + } + if(errorObj.timestamp && errorObj.stack){ + if(errorObj.timestamp + 1000 * 60 * 60 * 24 > (+new Date)){ + diag.push("Last error: " + errorObj.stack) + diag.push("Error date: " + new Date(errorObj.timestamp).toGMTString()) + }else{ + localStorage.removeItem("lastError") + } + } + diag.push("```") + var diag = diag.join("\n") + + if(navigator.userAgent.indexOf("Android") >= 0){ + var iframe = document.createElement("iframe") + this.diagTxt.appendChild(iframe) + var body = iframe.contentWindow.document.body + body.innerText = diag + + body.setAttribute("style", ` + font-family: monospace; + margin: 2px 0 0 2px; + white-space: pre-wrap; + word-break: break-all; + cursor: text; + `) + body.setAttribute("onblur", ` + getSelection().removeAllRanges() + `) + }else{ + this.textarea = document.createElement("textarea") + this.textarea.readOnly = true + this.textarea.value = diag + this.diagTxt.appendChild(this.textarea) + if(!this.touchEnabled){ + pageEvents.add(this.textarea, "focus", () => { + this.textarea.select() + }) + pageEvents.add(this.textarea, "blur", () => { + getSelection().removeAllRanges() + }) + } + } + + var issueBody = this.issueTemplate.join("\n") + "\n\n\n\n" + diag + this.getLink(this.linkGithub).href += "?body=" + encodeURIComponent(issueBody) + this.getLink(this.linkEmail).href += "?body=" + encodeURIComponent(issueBody.replace(/\n/g, "
\r\n")) + } + getLink(target){ + return target.getElementsByTagName("a")[0] + } + linkButton(event){ + this.getLink(event.currentTarget).click() + } + clean(){ + cancelTouch = true + this.gamepad.clean() + pageEvents.remove(this.linkGithub, ["click", "touchend"]) + pageEvents.remove(this.linkEmail, ["click", "touchend"]) + pageEvents.remove(this.endButton, ["mousedown", "touchstart"]) + if(this.textarea){ + pageEvents.remove(this.textarea, ["focus", "blur"]) + } + pageEvents.keyRemove(this, 13) + delete this.endButton + delete this.diagTxt + delete this.version + delete this.tutorialOuter + delete this.linkGithub + delete this.linkEmail + delete this.textarea + } +} diff --git a/public/src/js/assets.js b/public/src/js/assets.js index e289e16..fde1bec 100644 --- a/public/src/js/assets.js +++ b/public/src/js/assets.js @@ -123,7 +123,8 @@ var assets = { "loadsong.html", "songselect.html", "titlescreen.html", - "tutorial.html" + "tutorial.html", + "about.html" ], "songs": [], diff --git a/public/src/js/canvastest.js b/public/src/js/canvastest.js index b952981..dea0bf6 100644 --- a/public/src/js/canvastest.js +++ b/public/src/js/canvastest.js @@ -91,6 +91,7 @@ class CanvasTest{ drawAllImages(){ return new Promise(resolve => { requestAnimationFrame(() => { + var startTime = +new Date var ctx = this.ctx ctx.save() ctx.clearRect(0, 0, this.canvas.width, this.canvas.height) @@ -131,7 +132,7 @@ class CanvasTest{ } ctx.restore() - resolve() + resolve((+new Date) - startTime) }) }) } diff --git a/public/src/js/loader.js b/public/src/js/loader.js index e8bda9d..59954b6 100644 --- a/public/src/js/loader.js +++ b/public/src/js/loader.js @@ -5,16 +5,9 @@ class Loader{ this.assetsDiv = document.getElementById("assets") this.canvasTest = new CanvasTest() p2 = new P2Connection() - this.ajax("src/views/loader.html").then(this.run.bind(this)) + this.startTime = +new Date - pageEvents.add(root, ["touchstart", "touchmove", "touchend"], event => { - event.preventDefault() - }) - var versionDiv = document.getElementById("version") - var versionLink = document.getElementById("version-link") - pageEvents.add(versionDiv, ["click", "touchend"], () => { - versionLink.click() - }) + this.ajax("src/views/loader.html").then(this.run.bind(this)) } run(page){ this.promises = [] @@ -94,6 +87,7 @@ class Loader{ }) this.promises.push(this.canvasTest.blurPerformance().then(result => { + perf.blur = result if(result > 1000 / 50){ // Less than 50 fps with blur enabled disableBlur = true @@ -105,7 +99,9 @@ class Loader{ }) Promise.all(this.promises).then(() => { - this.canvasTest.drawAllImages().then(() => { + this.canvasTest.drawAllImages().then(result => { + perf.allImg = result + perf.load = (+new Date) - this.startTime this.canvasTest.clean() this.clean() this.callback() diff --git a/public/src/js/main.js b/public/src/js/main.js index 98f6121..7647985 100644 --- a/public/src/js/main.js +++ b/public/src/js/main.js @@ -1,3 +1,16 @@ +addEventListener("error", err => { + var stack + if("error" in err){ + stack = err.error.stack + }else{ + stack = err.message + "\n at " + err.filename + ":" + err.lineno + ":" + err.colno + } + localStorage["lastError"] = JSON.stringify({ + timestamp: +new Date, + stack: stack + }) +}) + function toggleFullscreen(){ if("requestFullscreen" in root){ if(document.fullscreenElement){ @@ -19,6 +32,14 @@ function toggleFullscreen(){ } } } + +function resizeRoot(){ + if(lastHeight !== innerHeight){ + lastHeight = innerHeight + root.style.height = innerHeight + "px" + } +} + var root = document.documentElement var fullScreenSupported = "requestFullscreen" in root || "webkitRequestFullscreen" in root || "mozRequestFullScreen" in root @@ -26,6 +47,27 @@ var pageEvents = new PageEvents() var snd = {} var p2 var disableBlur = false +var cancelTouch = true +var lastHeight +var perf = { + blur: 0, + allImg: 0, + load: 0 +} + +pageEvents.add(root, ["touchstart", "touchmove", "touchend"], event => { + if(event.cancelable && cancelTouch){ + event.preventDefault() + } +}) +var versionDiv = document.getElementById("version") +var versionLink = document.getElementById("version-link") +pageEvents.add(versionDiv, ["click", "touchend"], () => { + versionLink.click() +}) +resizeRoot() +setInterval(resizeRoot, 100) + var loader = new Loader(() => { new Titlescreen() }) diff --git a/public/src/js/scoresheet.js b/public/src/js/scoresheet.js index 41c57e2..f2b3fdd 100644 --- a/public/src/js/scoresheet.js +++ b/public/src/js/scoresheet.js @@ -115,7 +115,7 @@ class Scoresheet{ ctx.save() var winW = innerWidth - var winH = innerHeight + var winH = lastHeight this.pixelRatio = window.devicePixelRatio || 1 winW *= this.pixelRatio winH *= this.pixelRatio diff --git a/public/src/js/songselect.js b/public/src/js/songselect.js index 53248e9..61a4641 100644 --- a/public/src/js/songselect.js +++ b/public/src/js/songselect.js @@ -29,6 +29,12 @@ class SongSelect{ border: ["#d6ffff", "#6bae9c"], outline: "#31ae94" }, + "about": { + sort: 7, + background: "#91cfff", + border: ["#dff0ff", "#6890b2"], + outline: "#217abb" + }, "J-POP": { sort: 0, background: "#219fbb", @@ -114,7 +120,9 @@ class SongSelect{ category: "ランダム" }) if(touchEnabled){ - fromTutorial = false + if(fromTutorial === "tutorial"){ + fromTutorial = false + } }else{ this.songs.push({ title: "あそびかた説明", @@ -123,6 +131,12 @@ class SongSelect{ category: "ランダム" }) } + this.songs.push({ + title: "について", + skin: this.songSkin.about, + action: "about", + category: "ランダム" + }) this.songs.push({ title: "もどる", skin: this.songSkin.back, @@ -155,16 +169,20 @@ class SongSelect{ this.selectedDiff = 0 assets.sounds["bgm_songsel"].playLoop(0.1, false, 0, 1.442, 3.506) - if(touchEnabled || !fromTutorial && "selectedSong" in localStorage){ + if(!fromTutorial && !("selectedSong" in localStorage)){ + fromTutorial = touchEnabled ? "about" : "tutorial" + } + + if(fromTutorial){ + this.selectedSong = this.songs.findIndex(song => song.action === fromTutorial) + this.playBgm(true) + }else{ if("selectedSong" in localStorage){ this.selectedSong = Math.min(Math.max(0, localStorage["selectedSong"] |0), this.songs.length) } assets.sounds["song-select"].play() snd.musicGain.fadeOut() this.playBgm(false) - }else{ - this.selectedSong = this.songs.findIndex(song => song.action === "tutorial") - this.playBgm(true) } if("selectedDiff" in localStorage){ this.selectedDiff = Math.min(Math.max(0, localStorage["selectedDiff"] |0), 4) @@ -454,6 +472,8 @@ class SongSelect{ }, 200) }else if(currentSong.action === "tutorial"){ this.toTutorial() + }else if(currentSong.action === "about"){ + this.toAbout() } this.pointer(false) } @@ -505,6 +525,13 @@ class SongSelect{ new Tutorial(true) }, 500) } + toAbout(){ + assets.sounds["don"].play() + this.clean() + setTimeout(() => { + new About(this.touchEnabled) + }, 500) + } redraw(){ if(!this.redrawRunning){ @@ -538,7 +565,7 @@ class SongSelect{ var ctx = this.ctx var winW = innerWidth - var winH = innerHeight + var winH = lastHeight if(winW / 32 > winH / 9){ winW = winH / 9 * 32 } diff --git a/public/src/js/tutorial.js b/public/src/js/tutorial.js index 3f9e1e3..b340d2c 100644 --- a/public/src/js/tutorial.js +++ b/public/src/js/tutorial.js @@ -22,7 +22,7 @@ class Tutorial{ assets.sounds["don"].play() localStorage.setItem("tutorial", "true") setTimeout(() => { - new SongSelect(this.fromSongSel, false, touched) + new SongSelect(this.fromSongSel ? "tutorial" : false, false, touched) }, 500) } clean(){ diff --git a/public/src/js/view.js b/public/src/js/view.js index 99d37ae..a9f5dcf 100644 --- a/public/src/js/view.js +++ b/public/src/js/view.js @@ -949,8 +949,10 @@ class View{ } clean(){ pageEvents.mouseRemove(this) - if(this.controller.multiplayer === 2 && this.canvas){ - this.canvas.canvas.parentNode.removeChild(this.canvas.canvas) + if(this.controller.multiplayer === 2){ + if(this.canvas){ + this.canvas.canvas.parentNode.removeChild(this.canvas.canvas) + } }else{ this.cursor.parentNode.removeChild(this.cursor) } diff --git a/public/src/views/about.html b/public/src/views/about.html new file mode 100644 index 0000000..5777ea2 --- /dev/null +++ b/public/src/views/about.html @@ -0,0 +1,27 @@ +
+
+
について / About
+
+ このシミュレータは現在開発中です。
+ バグが発生した場合は、報告してください。
+ GitHubかメールでバグを報告してください。
+ 以下の診断情報を含めてください!
+
+ This simulator is still in development.
+ Please report any bugs you find.
+ You can report bugs either via GitHub or email.
+ Be sure to include the following diagnostic data! + +
+
+ +
OK
+
+
diff --git a/templates/index.html b/templates/index.html index d813baf..d4b8f13 100644 --- a/templates/index.html +++ b/templates/index.html @@ -15,7 +15,6 @@ 太鼓の達人ウェブ - Taiko no Tatsujin Web - @@ -52,6 +51,7 @@ +