Skip to content

Commit

Permalink
Add persistency and shareable url
Browse files Browse the repository at this point in the history
  • Loading branch information
Dianliang233 committed Jul 10, 2024
1 parent d31dc4e commit 4810d39
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 55 deletions.
2 changes: 2 additions & 0 deletions mediawiki/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ mw.hook('wikipage.content').add(() => {
)
} else if (event.data.type === 'mcw-calc-height-change') {
iframe.style.height = `${event.data.data.height}px`
} else if (event.data.type === 'mcw-calc-clipboard') {
navigator.clipboard.writeText(event.data.data.text)
}
})
})
Expand Down
159 changes: 104 additions & 55 deletions src/tools/banner/App.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
import CalcField from '@/components/CalcField.vue'
import { reactive, ref, watch } from 'vue'
import { onMounted, ref, watch } from 'vue'
import { CdxButton, CdxSelect, type MenuItemData, CdxTable, CdxIcon } from '@wikimedia/codex'
import { useI18n } from 'vue-i18n'
import { colorMap, colorRgbMap } from '@/utils/color/java'
Expand All @@ -9,11 +9,14 @@ import {
cdxIconAlert,
cdxIconDownTriangle,
cdxIconError,
cdxIconLink,
cdxIconTableAddRowAfter,
cdxIconTrash,
cdxIconUpTriangle,
} from '@wikimedia/codex-icons'
import BannerPopup from './BannerPopup.vue'
import { useLocalStorage } from '@vueuse/core'
import { isEmbedded, parentUrl } from '@/utils/iframe'
const props = defineProps<{ icon: 'banner' | 'shield' }>()
Expand Down Expand Up @@ -115,23 +118,26 @@ const patternName = {
guster: 'Guster',
}
const activePatterns = reactive<Pattern[]>([
const activePatterns = useLocalStorage<Pattern[]>('mcwBannerActivePatterns', [
{
id: 0,
name: 'mojang',
color: 'black',
},
])
function updatePattern(index: number, pattern: keyof typeof patternName) {
activePatterns[index].name = pattern
activePatterns.value[index].name = pattern
}
function updatePatternIds() {
activePatterns.forEach((pattern, index) => {
activePatterns.value.forEach((pattern, index) => {
pattern.id = index
})
}
function newLayer() {
activePatterns.push({ ...activePatterns[activePatterns.length - 1], id: activePatterns.length })
activePatterns.value.push({
...activePatterns.value[activePatterns.value.length - 1],
id: activePatterns.value.length,
})
}
const patternMenuItems: MenuItemData[] = patternId.map((pattern) => ({
Expand All @@ -150,10 +156,10 @@ const colorMenuItems: MenuItemData[] = Object.entries(colorMap).map((color) => (
`,
}))
function updateColor(index: number, color: Color) {
activePatterns[index].color = color
activePatterns.value[index].color = color
}
const baseColor = ref<Color>('white')
const baseColor = useLocalStorage<Color>('mcwBannerBaseColor', 'white')
const canvasRef = ref<HTMLCanvasElement | null>(null)
Expand Down Expand Up @@ -188,57 +194,93 @@ function imageToImageData(image: HTMLImageElement) {
return context.getImageData(0, 0, 20, 40)
}
watch([activePatterns, baseColor, canvasRef], async ([patterns, color, canvas]) => {
const baseColor = colorRgbMap[color]
if (!canvas) return
const ctx = canvas.getContext('2d', {
willReadFrequently: true,
})
if (!ctx) return
ctx.clearRect(0, 0, canvas.width, canvas.height)
const images = await promiseAllObject({
base: loadImage(
'https://minecraft.wiki/images/Banner_base_(texture)_JE1_BE1.png?format=original',
),
...Object.fromEntries(
patterns.map((pattern) => [
pattern.name,
loadImage(
`https://minecraft.wiki/images/Banner_${pattern.name}_(texture)_JE1_BE1.png?format=original`,
),
]),
),
})
watch(
[activePatterns, baseColor, canvasRef],
async ([patterns, color, canvas]) => {
const baseColor = colorRgbMap[color]
if (!canvas) return
const ctx = canvas.getContext('2d', {
willReadFrequently: true,
})
if (!ctx) return
ctx.drawImage(images.base, 1, 1, 20, 40, 0, 0, 20, 40)
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
const data = imageData.data
for (let i = 0; i < data.length; i += 4) {
data[i] = (data[i] * baseColor[0]) / 255
data[i + 1] = (data[i + 1] * baseColor[1]) / 255
data[i + 2] = (data[i + 2] * baseColor[2]) / 255
}
ctx.clearRect(0, 0, canvas.width, canvas.height)
for (const pattern of patterns) {
const patternImage = images[pattern.name]
const patternData = imageToImageData(patternImage)
const patternColor = colorRgbMap[pattern.color]
const patternDataArray = patternData.data
for (let i = 0; i < patternDataArray.length; i += 4) {
const red2 = (patternColor[0] / 255) * (patternDataArray[i] / 255)
const green2 = (patternColor[1] / 255) * (patternDataArray[i + 1] / 255)
const blue2 = (patternColor[2] / 255) * (patternDataArray[i + 2] / 255)
const alpha2 = patternDataArray[i + 3] / 255
data[i] = (red2 * alpha2 + (data[i] / 255) * (1 - alpha2)) * 255
data[i + 1] = (green2 * alpha2 + (data[i + 1] / 255) * (1 - alpha2)) * 255
data[i + 2] = (blue2 * alpha2 + (data[i + 2] / 255) * (1 - alpha2)) * 255
const images = await promiseAllObject({
base: loadImage(
'https://minecraft.wiki/images/Banner_base_(texture)_JE1_BE1.png?format=original',
),
...Object.fromEntries(
patterns.map((pattern) => [
pattern.name,
loadImage(
`https://minecraft.wiki/images/Banner_${pattern.name}_(texture)_JE1_BE1.png?format=original`,
),
]),
),
})
ctx.drawImage(images.base, 1, 1, 20, 40, 0, 0, 20, 40)
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
const data = imageData.data
for (let i = 0; i < data.length; i += 4) {
data[i] = (data[i] * baseColor[0]) / 255
data[i + 1] = (data[i + 1] * baseColor[1]) / 255
data[i + 2] = (data[i + 2] * baseColor[2]) / 255
}
for (const pattern of patterns) {
const patternImage = images[pattern.name]
const patternData = imageToImageData(patternImage)
const patternColor = colorRgbMap[pattern.color]
const patternDataArray = patternData.data
for (let i = 0; i < patternDataArray.length; i += 4) {
const red2 = (patternColor[0] / 255) * (patternDataArray[i] / 255)
const green2 = (patternColor[1] / 255) * (patternDataArray[i + 1] / 255)
const blue2 = (patternColor[2] / 255) * (patternDataArray[i + 2] / 255)
const alpha2 = patternDataArray[i + 3] / 255
data[i] = (red2 * alpha2 + (data[i] / 255) * (1 - alpha2)) * 255
data[i + 1] = (green2 * alpha2 + (data[i + 1] / 255) * (1 - alpha2)) * 255
data[i + 2] = (blue2 * alpha2 + (data[i + 2] / 255) * (1 - alpha2)) * 255
}
}
}
ctx.putImageData(imageData, 0, 0)
ctx.putImageData(imageData, 0, 0)
},
{
deep: true,
},
)
function copyShareUrl() {
const url = new URL(t('banner.shareUrl'))
const searchParams = new URLSearchParams()
searchParams.set('activePatterns', JSON.stringify(activePatterns.value))
searchParams.set('baseColor', baseColor.value)
url.hash = '?' + searchParams.toString()
if (isEmbedded()) {
window.parent.postMessage(
{
type: 'mcw-calc-clipboard',
data: { text: url.href },
},
'*',
)
} else navigator.clipboard.writeText(url.href)
}
onMounted(() => {
const url = parentUrl()
const params = new URLSearchParams(url.hash.slice(2))
const activePatternsParam = params.get('activePatterns')
if (activePatternsParam) {
activePatterns.value = JSON.parse(activePatternsParam)
}
const baseColorParam = params.get('baseColor')
if (baseColorParam) {
baseColor.value = baseColorParam as Color
}
})
</script>
<template>
Expand Down Expand Up @@ -391,7 +433,14 @@ watch([activePatterns, baseColor, canvasRef], async ([patterns, color, canvas])
</div>
</div>

<CdxSelect $selected="baseColor" class="w-[200px] mt-3" :menu-items="colorMenuItems" />
<div class="flex flex-row items-stretch gap-3 mt-3">
<CdxSelect $selected="baseColor" class="w-[200px]" :menu-items="colorMenuItems" />

<CdxButton @click="copyShareUrl">
<CdxIcon :icon="cdxIconLink" />
{{ t('banner.copyShareUrl') }}
</CdxButton>
</div>
</CalcField>
</template>
<style lang="less">
Expand Down
3 changes: 3 additions & 0 deletions src/tools/banner/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
"banner.error": "Error",
"banner.limitError": "The maximum number of layer obtainable with a command is 16. This layer is unobtainable in the game and is for demonstration purposes only.",

"banner.shareUrl": "https://minecraft.wiki/w/Calculators/Banner",
"banner.copyShareUrl": "Copy shareable link",

"banner.color.white": "White",
"banner.color.lightGray": "Light Gray",
"banner.color.gray": "Gray",
Expand Down
14 changes: 14 additions & 0 deletions src/utils/iframe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,17 @@ export function parentOrigin(): string {

return new URL(paramUrl || document.referrer || location.href).origin
}

export function parentUrl(): URL {
const paramUrl = new URLSearchParams(window.location.hash.substring(2)).get('url')

return new URL(paramUrl || document.referrer || location.href)
}

export function isEmbedded() {
try {
return window.self !== window.top
} catch (e) {
return true
}
}

0 comments on commit 4810d39

Please sign in to comment.