use game font for preview
This commit is contained in:
parent
5f81c7b9b1
commit
4589c52349
9 changed files with 2641 additions and 23 deletions
|
@ -11,7 +11,8 @@
|
|||
<span v-else>{{ getLabel('codeinfo.author') }} {{ translatedCode.author }}</span>
|
||||
</div>
|
||||
<p class="description" v-html="translatedCode.description"></p>
|
||||
<component v-if="configUI" :is="configUI" :version="version"></component>
|
||||
<component v-if="configUI" :is="configUI" :version="version"
|
||||
:codeConfigs="codeConfigs" @config="$emit('config', {[code.id]: $event})" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -24,6 +25,7 @@ export default {
|
|||
anchor: { type: Boolean },
|
||||
code: { type: Object },
|
||||
version: { type: String },
|
||||
codeConfigs: { type: Object },
|
||||
},
|
||||
computed: {
|
||||
translatedCode: function () {
|
||||
|
|
|
@ -42,7 +42,8 @@
|
|||
|
||||
<div v-if="codes && codes.length > 0" class="help">
|
||||
<h3>{{ getLabel('headers.help') }}</h3>
|
||||
<CodeInfo v-if="!!inspectingCode" :code="inspectingCode" :version="selectedVersion" />
|
||||
<CodeInfo v-if="!!inspectingCode" :code="inspectingCode" :version="selectedVersion"
|
||||
:codeConfigs="codeConfigs" @config="onCodeConfigChanged" />
|
||||
<div v-else-if="showStageLoaderHelp">
|
||||
<h3>{{ getLabel('headers.stageloader') }}</h3>
|
||||
<div>
|
||||
|
@ -109,6 +110,9 @@ import gameVersions from '../data/gameVersions.json';
|
|||
// Util
|
||||
import { translate } from '../i18n/localeHelper';
|
||||
|
||||
// Code Configs
|
||||
import {getConfig as qftGetConfig} from './codes/qft/codegen';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
|
@ -122,6 +126,12 @@ export default {
|
|||
stageLoaderCodes: [],
|
||||
showStageLoaderHelp: false,
|
||||
generation: 0,
|
||||
codeConfigs: {},
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.codeConfigs = {
|
||||
qft: qftGetConfig(),
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
@ -192,6 +202,10 @@ export default {
|
|||
this.showStageLoaderHelp = false;
|
||||
this.inspectingCode = code;
|
||||
},
|
||||
|
||||
onCodeConfigChanged(e) {
|
||||
this.codeConfigs = {...this.codeConfigs, ...e};
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
66
site/.vuepress/components/Preview.vue
Normal file
66
site/.vuepress/components/Preview.vue
Normal file
|
@ -0,0 +1,66 @@
|
|||
<template>
|
||||
<div class="preview-root">
|
||||
<div class="preview-ctn">
|
||||
<div :style="qft.bgStyle" />
|
||||
<PreviewString v-if="qft" :x="qft.x" :y="qft.y" :size="qft.fontSize" :color="qft.color" text="0:00:00" />
|
||||
<PreviewString v-if="mdp" :x="mdp.x" :y="mdp.y" :size="mdp.fontSize" :color="mdp.color" :text="mdp.text" />
|
||||
<PreviewString :x="16" :y="320" :size="20" :color="'#fff'" text="Pattern #0 0 0" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
config: {type: Object},
|
||||
},
|
||||
computed: {
|
||||
mdp() {
|
||||
return {
|
||||
x: 16,
|
||||
y: 200,
|
||||
fontSize: 20,
|
||||
color: '#fff',
|
||||
text: 'X Pos -4\nY Pos 27\nZ Pos -1515\nAngle 3117\nH Spd 4.26\nV Spd 4.28',
|
||||
};
|
||||
},
|
||||
qft() {
|
||||
const {config: {qft}} = this;
|
||||
if (qft == null) return;
|
||||
const {x, y, fontSize, fgRGB, fgA, fgRGB2, fgA2, bgRGB, bgA, width} = qft;
|
||||
const i2s = (rgb, a) => '#'+rgb.toString(16).padStart('0', 6)+a.toString(16).padStart('0', 2);
|
||||
const bg = i2s(bgRGB, bgA);
|
||||
const fg1 = i2s(fgRGB, fgA);
|
||||
return {
|
||||
x, y, fontSize,
|
||||
color: fgRGB2==null || fgA2==null ? fg1 : `linear-gradient(180deg, ${fg1}, ${i2s(fgRGB2, fgA2)})`,
|
||||
bgStyle: {
|
||||
left: x+'px',
|
||||
top: (y-fontSize)+'px',
|
||||
width: (width*fontSize/20)+'px',
|
||||
height: fontSize+'px',
|
||||
background: bg,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
div.preview-root {
|
||||
position: relative;
|
||||
width: 600px;
|
||||
height: 448px;
|
||||
background: url(/img/preview/background.png);
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
div.preview-ctn {
|
||||
position: absolute;
|
||||
top: -16px;
|
||||
}
|
||||
div.preview-ctn * {
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
97
site/.vuepress/components/PreviewString.vue
Normal file
97
site/.vuepress/components/PreviewString.vue
Normal file
|
@ -0,0 +1,97 @@
|
|||
<template>
|
||||
<div class="preview-str" :style="styles.root" >
|
||||
<div v-for="style, i in styles.chars" :key="i" class="char-ctn" :style="style.ctn">
|
||||
<div class="char-bg" :style="style.bg" />
|
||||
<div class="char-mask" :style="style.mask" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import charInfo from '../data/font-jp.json';
|
||||
export default {
|
||||
props: {
|
||||
x: {type: Number},
|
||||
y: {type: Number},
|
||||
size: {type: Number},
|
||||
color: {type: String},
|
||||
text: {type: String},
|
||||
},
|
||||
computed: {
|
||||
styles() {
|
||||
const {x: x0, y: y0, size: fontSize, color, text} = this;
|
||||
|
||||
/** @type {{x: number, y: number, u: number, v: number}[]} */
|
||||
const chars = [];
|
||||
let x = 0;
|
||||
let y = 0;
|
||||
let useKerning = false;
|
||||
text.split('').forEach(c => {
|
||||
const {index, kerning, width} = charInfo[c] ?? charInfo[' '];
|
||||
if (c === '\n') {
|
||||
useKerning = false;
|
||||
x = 0;
|
||||
y += 20;
|
||||
return;
|
||||
}
|
||||
if (useKerning) x -= kerning;
|
||||
useKerning = true;
|
||||
// uv
|
||||
const [u, v] = [index%25*20, (index/25|0)*20];
|
||||
chars.push({x, y, u, v});
|
||||
// next
|
||||
x += width + kerning;
|
||||
});
|
||||
|
||||
return {
|
||||
root: {
|
||||
transform: `translate(${x0}px, ${y0-fontSize}px) scale(${fontSize/20})`,
|
||||
},
|
||||
chars: chars.map(({x, y, u, v}) => {
|
||||
const offset = `${-u}px ${-v}px`;
|
||||
return {
|
||||
ctn: {
|
||||
left: x+'px',
|
||||
top: y+'px',
|
||||
},
|
||||
bg: {
|
||||
'background-position': offset,
|
||||
},
|
||||
mask: {
|
||||
'mask-position': offset,
|
||||
'-webkit-mask-position': offset,
|
||||
background: color,
|
||||
},
|
||||
};
|
||||
}),
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.preview-str {
|
||||
position: relative;
|
||||
}
|
||||
.preview-str * {
|
||||
position: absolute;
|
||||
}
|
||||
div.char-ctn {
|
||||
isolation: isolate;
|
||||
}
|
||||
div.char-ctn > div {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
div.char-bg {
|
||||
background: url(/img/preview/font-jp.png);
|
||||
}
|
||||
div.char-mask {
|
||||
mask-image: url(/img/preview/font-jp.png);
|
||||
-webkit-mask-image: url(/img/preview/font-jp.png);
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mix-blend-mode: multiply;
|
||||
}
|
||||
</style>
|
|
@ -26,7 +26,8 @@ export const defaultConfig = {
|
|||
};
|
||||
|
||||
export function getConfig() {
|
||||
const config = parseJSON(localStorage.getItem(lskey)) ?? {};
|
||||
const config =
|
||||
(typeof localStorage !== 'undefined' && parseJSON(localStorage.getItem(lskey))) || {};
|
||||
return {
|
||||
...defaultConfig,
|
||||
...config,
|
||||
|
|
|
@ -22,21 +22,7 @@
|
|||
<span>{{l.alpha}}</span><input type="number" min="0" max="255" v-model.number="bgA"><span>/255={{(bgA/2.55).toFixed(1)}}%</span>
|
||||
</div>
|
||||
<h4>{{l.preview}}</h4>
|
||||
<svg viewBox="0 16 600 448">
|
||||
<defs>
|
||||
<linearGradient id="fgColor" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" :style="{stopColor: rgbI2S(fgRGB), stopOpacity: fgA/255}" />
|
||||
<stop offset="100%" :style="{
|
||||
stopColor: rgbI2S(fgRGB2 == null ? fgRGB : fgRGB2),
|
||||
stopOpacity: (fgA2 == null ? fgA : fgA2)/255,
|
||||
}" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<image href="/img/qft/preview-base.jpg" y="16" width="600" height="448" />
|
||||
<rect :x="x" :y="y-fontSize" :width="width*fontSize/20" :height="fontSize" :fill="rgbaI2S(bgRGB, bgA)" />
|
||||
<text :x="x+fontSize/10" :y="y-fontSize/10" fill="url(#fgColor)"
|
||||
:style="{fontSize: fontSize+'px', fontFamily: 'auto'}">0:00.000</text>
|
||||
</svg>
|
||||
<Preview :config="codeConfigs" />
|
||||
<div style="white-space: pre">{{l.previewNote}}</div>
|
||||
</section>
|
||||
<section class="freeze">
|
||||
|
@ -58,23 +44,25 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import {getConfig, lskey, buttonValues, codes} from './codegen.js';
|
||||
// import Preview from '../../PreviewString.vue';
|
||||
import {getConfig, lskey, codes} from './codegen.js';
|
||||
import labels from './labels.json';
|
||||
import {getLabels} from '../codegen.js';
|
||||
|
||||
function updateConfig() {
|
||||
const {x, y, fontSize, width, fgRGB, fgA, fgRGB2, fgA2, bgRGB, bgA, freeze, freezeDuration} = this;
|
||||
localStorage.setItem(lskey, JSON.stringify({
|
||||
const config = {
|
||||
x, y, fontSize, width, fgRGB, fgA, fgRGB2, fgA2, bgRGB, bgA, freeze, freezeDuration
|
||||
}));
|
||||
};
|
||||
localStorage.setItem(lskey, JSON.stringify(config));
|
||||
this.$emit('config', config);
|
||||
}
|
||||
|
||||
export default {
|
||||
props: {
|
||||
version: {type: String},
|
||||
codeConfigs: {type: Object},
|
||||
},
|
||||
methods: {
|
||||
updateConfig,
|
||||
onChangeFreeze($event, key) {
|
||||
this.freeze[key] = $event.target.checked;
|
||||
this.updateConfig();
|
||||
|
|
2450
site/.vuepress/data/font-jp.json
Normal file
2450
site/.vuepress/data/font-jp.json
Normal file
File diff suppressed because it is too large
Load diff
BIN
site/.vuepress/public/img/preview/background.png
Normal file
BIN
site/.vuepress/public/img/preview/background.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 505 KiB |
BIN
site/.vuepress/public/img/preview/font-jp.png
Normal file
BIN
site/.vuepress/public/img/preview/font-jp.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 74 KiB |
Loading…
Reference in a new issue