Skip to content

Commit

Permalink
feat: 补充画布功能
Browse files Browse the repository at this point in the history
  • Loading branch information
Otto-J committed Oct 12, 2023
1 parent e528403 commit 410d15a
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 38 deletions.
19 changes: 16 additions & 3 deletions components/c-side-bar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,26 @@
</template>
</var-cell>
</nuxt-link>
<nuxt-link to="/tools">
<var-cell ripple title="Tools">
<template #icon>
<icon
name="material-symbols:auto-label-outline"
size="18"
class="mr-2"
/>
</template>
</var-cell>
</nuxt-link>
</var-paper>
</div>
<var-paper
class="pt-2 pb-6 text-center text-gray-800 dark:text-gray-100"
class="pt-2 mx-2 lg:mx-0 pb-6 text-center text-gray-800 dark:text-gray-100"
:elevation="2"
>
<var-image width="50%" class="mx-auto" src="/boy.png" />
<p class="text-lg">{{ appConfig.website_author_name }}</p>
<p class="text-sm">{{ appConfig.website_author_desc }}</p>
<p class="text-lg text-gray-600">{{ appConfig.website_author_name }}</p>
<p class="text-sm text-gray-500">{{ appConfig.website_author_desc }}</p>

<div class="flex justify-center items-center space-x-2 mt-2">
<div v-for="item of socialLinks" :key="item.link">
Expand All @@ -99,6 +110,8 @@
/>
</div>
</div>

<p class="mt-4 text-sm text-gray-500">最近在学 Nuxt.js</p>
</var-paper>
</div>
</div>
Expand Down
24 changes: 24 additions & 0 deletions components/ui/checkbox/Checkbox.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<script setup lang="ts">
import type { CheckboxRootEmits, CheckboxRootProps } from 'radix-vue'
import { CheckboxIndicator, CheckboxRoot, useEmitAsProps } from 'radix-vue'
import { Check } from 'lucide-vue-next'
import { cn } from '@/utils'
const props = defineProps<CheckboxRootProps>()
const emits = defineEmits<CheckboxRootEmits>()
const emitsAsProps = useEmitAsProps(emits)
</script>

<template>
<CheckboxRoot
v-bind="{ ...props, ...emitsAsProps }"
:class="
cn('peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground',
$attrs.class ?? '')"
>
<CheckboxIndicator class="flex items-center justify-center text-current">
<Check class="h-4 w-4" />
</CheckboxIndicator>
</CheckboxRoot>
</template>
1 change: 1 addition & 0 deletions components/ui/checkbox/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as Checkbox } from './Checkbox.vue'
4 changes: 2 additions & 2 deletions layouts/default.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<template>
<div class="container mx-auto layout-default">
<div class="container px-0 layout-default">
<div
class="flex justify-between align-top md:space-x-4 flex-col md:flex-row"
>
<div class="md:w-3/12 w-full">
<c-side-bar />
</div>
<div class="md:w-9/12 w-full">
<div class="md:w-9/12 w-full px-2">
<slot />
</div>
</div>
Expand Down
11 changes: 10 additions & 1 deletion nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export default defineNuxtConfig({
meta: [
{
name: "author",
content: "",
content: "辛宝Otto",
},
],
link: [
Expand Down Expand Up @@ -129,6 +129,15 @@ export default defineNuxtConfig({
href: "/feed.atom",
},
],
script: [
`var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?91755caf20b30881d156e53c364f4be7";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();`,
],
},
},
nitro: {
Expand Down
137 changes: 105 additions & 32 deletions pages/tools/canvas.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,27 @@
</UiCardHeader>
<UiCardContent class="space-y-4">
<div class="space-y-2">
<div class="flex space-x-2">
<div class="flex space-x-4">
<div class="w-1/3">
<UiLabel for="width">宽 Width {{ config.width[0] }}px</UiLabel>
<UiLabel for="config-width"
>宽 Width {{ config.width[0] }}px</UiLabel
>
<UiSlider
id="config-width"
class="h-4"
v-model="config.width"
:min="1"
:max="1000"
:step="1"
/>
</div>
<div class="w-1/3">
<UiLabel for="width"
<UiLabel for="config-height"
>高 Height {{ config.height[0] }}px</UiLabel
>
<UiSlider
id="config-height"
class="h-4"
v-model="config.height"
:min="1"
:max="1000"
Expand All @@ -31,21 +37,35 @@
</div>
</div>
</div>
<div class="flex">
<div class="flex space-x-4 items-center">
<div class="w-1/3 space-y-2">
<UiLabel for="config-background"
>背景颜色 {{ config.hue[0] }}</UiLabel
>
<UiSlider v-model="config.hue" :min="0" :max="359" :step="1" />
<UiSlider
id="config-background"
class="h-4"
v-model="config.hue"
:min="0"
:max="359"
:step="1"
/>
</div>
<div class="w-1/3">
<UiButton size="sm" variant="secondary" @click="rollBackground"
>颜色 Roll</UiButton
>
</div>
</div>
<div class="flex space-x-2">
<div class="flex space-x-4">
<div class="space-y-2 w-1/3">
<UiLabel for="config-color">
<UiLabel for="config-person-size">
小人大小 {{ config.personSize[0] }}
</UiLabel>
<div>
<UiSlider
id="config-person-size"
class="h-4"
v-model="config.personSize"
:min="1"
:max="Math.min(config.width[0], config.height[0])"
Expand All @@ -54,11 +74,13 @@
</div>
</div>
<div class="space-y-2 w-1/3">
<UiLabel for="config-color"
<UiLabel for="config-person-y"
>小人 y 轴 {{ config.personY[0] }}</UiLabel
>
<div>
<UiSlider
id="config-person-y"
class="h-4"
v-model="config.personY"
:min="-100"
:max="100"
Expand All @@ -67,17 +89,15 @@
</div>
</div>
</div>
<div class="flex space-x-2">
<div class="space-y-2 w-1/3">
<UiLabel for="config-color">标题内容</UiLabel>
<UiTextarea v-model="config.text" />
</div>
<div class="flex space-x-4">
<div class="space-y-2 w-1/3">
<UiLabel for="config-color"
<UiLabel for="config-text-y"
>文字 y 轴 {{ config.textY[0] }}</UiLabel
>
<div>
<UiSlider
id="config-text-y"
class="h-4"
v-model="config.textY"
:min="1"
:max="100"
Expand All @@ -86,45 +106,71 @@
</div>
</div>
<div class="space-y-2 w-1/3">
<UiLabel for="config-color"
<UiLabel for="config-text-size"
>文字大小 {{ config.fontSize[0] }}</UiLabel
>
<div>
<UiSlider
id="config-text-size"
v-model="config.fontSize"
class="h-4"
:min="1"
:max="100"
:step="1"
/>
</div>
</div>
</div>

<div class="space-y-2 w-full text-right">
<UiLabel for="config-text">标题内容</UiLabel>
<UiTextarea id="config-text" v-model="config.text" />
</div>

<div class="space-y-2 w-full">
<UiLabel for="config-from-text">角标</UiLabel>
<div class="flex items-center space-x-4">
<UiInput
id="config-from-text"
class="w-40"
v-model="config.fromText"
/>
<UiCheckbox
id="config-from-enable"
v-model:checked="config.fromEnable"
></UiCheckbox>
<UiLabel for="config-from-enable">是否显示</UiLabel>
</div>
</div>
</UiCardContent>
<UiCardFooter>
<UiButton @click="onClickPng">确定</UiButton>
<UiCardFooter class="flex space-x-2 items-center">
<UiButton @click="onClickPng">生成截图</UiButton>
<UiButton variant="destructive" @click="reset">重置</UiButton>
</UiCardFooter>
</UiCard>
</div>
<div>
<div class="overflow-x-auto" style="max-width: 100vw">
<p>原始内容</p>

<div
ref="raw"
class="flex justify-center items-center flex-col relative overflow-hidden"
class="flex justify-center items-center flex-col relative transition-all duration-500"
:style="{
width: config.width[0] + 'px',
height: config.height[0] + 'px',
background: 'hsl(' + config.hue[0] + ',48.1%,48.6%)',
color: config.text,
willChange: 'background',
}"
>
<div
class="absolute z-20 w-full text-center bg-white whitespace-pre-line left-0 -translate-y-1/2"
v-html="config.text"
class="absolute z-20 w-full text-center bg-white whitespace-pre-line left-0 -translate-y-1/2 transition-all duration-500"
v-text="config.text"
:style="{
color: 'hsl(' + config.hue[0] + ',48.1%,48.6%)',
fontSize: config.fontSize[0] + 'px',
top: config.textY[0] + '%',
willChange: 'color',
}"
></div>
<div
Expand All @@ -143,6 +189,12 @@
}"
/>
</div>
<div
v-show="config.fromEnable"
class="absolute right-3 bottom-3 z-10 text-white text-sm"
>
{{ config.fromText }}
</div>
</div>

<p>生成的图片:</p>
Expand All @@ -151,8 +203,7 @@
</ClientOnly>
</template>
<script lang="ts" setup>
import { toPng, toJpeg, toBlob, toPixelData, toSvg } from "html-to-image";
// import Button from "~/components/button/Button.vue";
import { toPng } from "html-to-image";
const node = ref<HTMLDivElement>();
const raw = ref<HTMLDivElement>();
Expand All @@ -161,7 +212,7 @@ const raw = ref<HTMLDivElement>();
// 200,48.1,48.6
// hsl(180,48.1%,48.6%)
const config = ref({
const defaultModel = () => ({
width: [600],
height: [250],
background: "blue",
Expand All @@ -171,11 +222,30 @@ const config = ref({
personY: [0],
textY: [10],
fontSize: [22],
fromText: "@webworker.tech",
fromEnable: true,
});
const config = ref(defaultModel());
const route = useRoute();
const router = useRouter();
watch(
() => config,
(val) => {
const _config = JSON.stringify(val.value);
router.replace({
query: {
q: _config,
},
});
},
{
deep: true,
},
);
onMounted(() => {
try {
const _q = route.query.q;
Expand All @@ -188,14 +258,9 @@ onMounted(() => {
}
});
watchEffect(() => {
// route.query.q = String(config.value);
router.replace({
query: {
q: JSON.stringify(config.value),
},
});
});
const rollBackground = () => {
config.value.hue[0] = Math.floor(Math.random() * 360);
};
const onClickPng = () => {
if (node.value && raw.value) {
Expand All @@ -210,6 +275,14 @@ const onClickPng = () => {
});
}
};
const reset = () => {
// 清空 node.value 的 所有内容
if (node.value) {
node.value.innerHTML = "";
}
config.value = defaultModel();
};
</script>

<style></style>
Loading

0 comments on commit 410d15a

Please sign in to comment.