界面样式调整
This commit is contained in:
parent
d70b962ef5
commit
cdf5f81601
78
README.md
78
README.md
@ -1,62 +1,62 @@
|
||||
# OBS Overlay Widget
|
||||
# OBS 悬浮小组件
|
||||
|
||||
A collection of highly customizable widgets for OBS Studio streaming and recording, built with Vue 3, TypeScript, and Vite.
|
||||
一个为 OBS Studio 直播和录制场景开发的高度可定制化小组件集合,基于 Vue 3、TypeScript 和 Vite 构建。
|
||||
|
||||
## Features
|
||||
## 功能特点
|
||||
|
||||
- **Time and Date Display**: Customizable formats for showing current time and date
|
||||
- **Text Display**: Show fixed text or API-returned content with custom styling
|
||||
- **Image Display**: Display local or remote images with customization options
|
||||
- **Split View Interface**: Configuration panel on the left, real-time preview on the right
|
||||
- **Transparent Background**: All widgets have transparent backgrounds suitable for OBS overlay
|
||||
- **URL Generation**: Automatically generates sharable URLs with encoded configuration
|
||||
- **Pure Preview Mode**: Open generated URLs to display only the widget with transparent background
|
||||
- **时间和日期显示**:可自定义格式的时间和日期显示
|
||||
- **文本显示**:展示固定文字或 API 返回内容,支持自定义样式
|
||||
- **图片显示**:展示本地或远程图片,支持自定义设置
|
||||
- **分屏界面**:左侧为配置面板,右侧为实时预览
|
||||
- **透明背景**:所有小组件均具有适合 OBS 悬浮的透明背景
|
||||
- **URL 生成**:自动生成包含编码配置的可分享 URL
|
||||
- **纯预览模式**:打开生成的 URL 仅显示小组件内容,无配置界面,背景透明
|
||||
|
||||
## Widget Types
|
||||
## 小组件类型
|
||||
|
||||
1. **Clock Widget**: Display current time with customizable format, style, and effects
|
||||
2. **Date Widget**: Show current date with customizable format, style, and effects
|
||||
3. **Text Widget**: Display text with customizable styles including gradients, shadows, and fonts
|
||||
4. **Image Widget**: Show images with customizable size, effects, and positioning
|
||||
1. **时钟小组件**:显示当前时间,可自定义格式、样式和特效
|
||||
2. **日期小组件**:显示当前日期,可自定义格式、样式和特效
|
||||
3. **文本小组件**:显示文本,支持渐变、阴影、字体等自定义样式
|
||||
4. **图片小组件**:显示图片,可自定义大小、特效和位置
|
||||
|
||||
## Usage
|
||||
## 使用方法
|
||||
|
||||
1. Select a widget type from the dropdown
|
||||
2. Configure the widget using the control panel on the left
|
||||
3. See real-time preview on the right
|
||||
4. Copy the generated URL to use in OBS Studio as a Browser Source
|
||||
1. 从下拉菜单中选择小组件类型
|
||||
2. 使用左侧控制面板配置小组件
|
||||
3. 在右侧实时查看预览效果
|
||||
4. 复制生成的 URL,在 OBS Studio 中作为浏览器源使用
|
||||
|
||||
## Development
|
||||
## 开发
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
# 安装依赖
|
||||
npm install
|
||||
|
||||
# Start development server
|
||||
# 启动开发服务器
|
||||
npm run dev
|
||||
|
||||
# Build for production
|
||||
# 构建生产版本
|
||||
npm run build
|
||||
|
||||
# Preview production build
|
||||
# 预览生产构建
|
||||
npm run preview
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
## 故障排除
|
||||
|
||||
If you encounter TypeScript errors related to undefined properties, make sure that:
|
||||
如果遇到与未定义属性相关的 TypeScript 错误,请确保:
|
||||
|
||||
1. All widget components handle possible undefined configuration properties
|
||||
2. Default values are provided for all configuration options
|
||||
3. Use proper null checking (e.g., `props.config.property || defaultValue`)
|
||||
1. 所有小组件组件都能处理可能未定义的配置属性
|
||||
2. 为所有配置选项提供默认值
|
||||
3. 使用适当的空值检查(例如:`props.config.property || defaultValue`)
|
||||
|
||||
## Integration with OBS Studio
|
||||
## 与 OBS Studio 集成
|
||||
|
||||
1. Run this application on a web server or locally
|
||||
2. Configure your widget using the configuration interface
|
||||
3. Copy the generated URL
|
||||
4. In OBS Studio:
|
||||
- Add a "Browser Source" to your scene
|
||||
- Paste the URL into the Browser Source URL field
|
||||
- Set width and height according to your needs
|
||||
- Check "Shutdown source when not visible" for better performance
|
||||
1. 在 Web 服务器或本地运行此应用
|
||||
2. 使用配置界面设置您的小组件
|
||||
3. 复制生成的 URL
|
||||
4. 在 OBS Studio 中:
|
||||
- 向您的场景添加"浏览器源"
|
||||
- 将 URL 粘贴到浏览器源 URL 字段
|
||||
- 根据需要设置宽度和高度
|
||||
- 勾选"不可见时关闭源"以获得更好的性能
|
||||
|
@ -1,28 +1,47 @@
|
||||
<template>
|
||||
<div class="clock-config">
|
||||
<h2>Clock Widget Settings</h2>
|
||||
<h2>时钟小组件设置</h2>
|
||||
|
||||
<el-form label-position="top">
|
||||
<el-form-item label="Time Format">
|
||||
<el-select v-model="localConfig.format" placeholder="Select format">
|
||||
<el-option label="HH:mm:ss (24-hour)" value="HH:mm:ss" />
|
||||
<el-option label="HH:mm (24-hour)" value="HH:mm" />
|
||||
<el-option label="hh:mm:ss A (12-hour)" value="hh:mm:ss A" />
|
||||
<el-option label="hh:mm A (12-hour)" value="hh:mm A" />
|
||||
<el-form-item label="时间格式">
|
||||
<el-select v-model="localConfig.format" placeholder="选择格式">
|
||||
<el-option label="HH:mm:ss (24小时制)" value="HH:mm:ss" />
|
||||
<el-option label="HH:mm (24小时制)" value="HH:mm" />
|
||||
<el-option label="hh:mm:ss A (12小时制)" value="hh:mm:ss A" />
|
||||
<el-option label="hh:mm A (12小时制)" value="hh:mm A" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Show Seconds">
|
||||
<el-switch v-model="localConfig.showSeconds" />
|
||||
<el-form-item>
|
||||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<div>
|
||||
<span>显示秒</span>
|
||||
<el-switch v-model="localConfig.showSeconds" style="margin-left: 10px;" />
|
||||
</div>
|
||||
<div>
|
||||
<span>显示日期</span>
|
||||
<el-switch v-model="localConfig.showDate" style="margin-left: 10px;" />
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<el-divider>Text Style</el-divider>
|
||||
<el-form-item v-if="localConfig.showDate" label="日期格式">
|
||||
<el-select v-model="localConfig.dateFormat" placeholder="选择格式">
|
||||
<el-option label="YYYY-MM-DD" value="YYYY-MM-DD" />
|
||||
<el-option label="MM/DD/YYYY" value="MM/DD/YYYY" />
|
||||
<el-option label="DD/MM/YYYY" value="DD/MM/YYYY" />
|
||||
<el-option label="MMMM D, YYYY" value="MMMM D, YYYY" />
|
||||
<el-option label="D MMMM YYYY" value="D MMMM YYYY" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Font Size">
|
||||
<el-divider>文本样式</el-divider>
|
||||
|
||||
<el-form-item label="字体大小">
|
||||
<el-slider v-model="localConfig.fontSize" :min="12" :max="120" show-input />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Font Family">
|
||||
<el-form-item label="字体">
|
||||
<el-select v-model="localConfig.fontFamily">
|
||||
<el-option label="Arial" value="Arial" />
|
||||
<el-option label="Helvetica" value="Helvetica" />
|
||||
@ -34,58 +53,58 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Font Weight">
|
||||
<el-form-item label="字重">
|
||||
<el-select v-model="localConfig.fontWeight">
|
||||
<el-option label="Normal" value="normal" />
|
||||
<el-option label="Bold" value="bold" />
|
||||
<el-option label="Light" value="lighter" />
|
||||
<el-option label="普通" value="normal" />
|
||||
<el-option label="粗体" value="bold" />
|
||||
<el-option label="细体" value="lighter" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-divider>Color Settings</el-divider>
|
||||
<el-divider>颜色设置</el-divider>
|
||||
|
||||
<el-form-item label="Use Gradient Colors">
|
||||
<el-form-item label="使用渐变色">
|
||||
<el-switch v-model="localConfig.useGradient" />
|
||||
</el-form-item>
|
||||
|
||||
<template v-if="!localConfig.useGradient">
|
||||
<el-form-item label="Text Color">
|
||||
<el-form-item label="文本颜色">
|
||||
<el-color-picker v-model="localConfig.color" show-alpha />
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<el-form-item label="Gradient Start Color">
|
||||
<el-form-item label="渐变起始颜色">
|
||||
<el-color-picker v-model="localConfig.gradientColors[0]" show-alpha />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Gradient End Color">
|
||||
<el-form-item label="渐变结束颜色">
|
||||
<el-color-picker v-model="localConfig.gradientColors[1]" show-alpha />
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<el-divider>Effects</el-divider>
|
||||
<el-divider>特效</el-divider>
|
||||
|
||||
<el-form-item label="Text Shadow">
|
||||
<el-form-item label="文字阴影">
|
||||
<el-switch v-model="localConfig.textShadow" />
|
||||
</el-form-item>
|
||||
|
||||
<template v-if="localConfig.textShadow">
|
||||
<el-form-item label="Shadow Color">
|
||||
<el-form-item label="阴影颜色">
|
||||
<el-color-picker v-model="localConfig.shadowColor" show-alpha />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Shadow Blur">
|
||||
<el-form-item label="阴影模糊度">
|
||||
<el-slider v-model="localConfig.shadowBlur" :min="0" :max="20" show-input />
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<el-form-item>
|
||||
<el-button-group>
|
||||
<el-button type="primary" @click="applyPreset('modern')">Modern</el-button>
|
||||
<el-button type="success" @click="applyPreset('neon')">Neon</el-button>
|
||||
<el-button type="warning" @click="applyPreset('elegant')">Elegant</el-button>
|
||||
<el-button type="danger" @click="applyPreset('minimal')">Minimal</el-button>
|
||||
<el-button type="primary" @click="applyPreset('modern')">现代风格</el-button>
|
||||
<el-button type="success" @click="applyPreset('neon')">霓虹风格</el-button>
|
||||
<el-button type="warning" @click="applyPreset('elegant')">优雅风格</el-button>
|
||||
<el-button type="danger" @click="applyPreset('minimal')">简约风格</el-button>
|
||||
</el-button-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@ -95,7 +114,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch, onMounted } from 'vue';
|
||||
|
||||
// Define props interface for config
|
||||
// 为配置定义 props 接口
|
||||
interface ClockConfig {
|
||||
format: string;
|
||||
color: string;
|
||||
@ -108,6 +127,8 @@ interface ClockConfig {
|
||||
useGradient: boolean;
|
||||
gradientColors: string[];
|
||||
showSeconds: boolean;
|
||||
showDate: boolean;
|
||||
dateFormat: string;
|
||||
}
|
||||
|
||||
// Define props with default values
|
||||
@ -125,7 +146,9 @@ const props = withDefaults(defineProps<{
|
||||
shadowBlur: 4,
|
||||
useGradient: false,
|
||||
gradientColors: ['#ff0000', '#0000ff'],
|
||||
showSeconds: true
|
||||
showSeconds: true,
|
||||
showDate: false,
|
||||
dateFormat: 'YYYY-MM-DD'
|
||||
})
|
||||
});
|
||||
|
||||
@ -146,7 +169,9 @@ const localConfig = ref<ClockConfig>({
|
||||
shadowBlur: 4,
|
||||
useGradient: false,
|
||||
gradientColors: ['#ff0000', '#0000ff'],
|
||||
showSeconds: true
|
||||
showSeconds: true,
|
||||
showDate: false,
|
||||
dateFormat: 'YYYY-MM-DD'
|
||||
});
|
||||
|
||||
// Sync with parent config on mount
|
||||
@ -167,7 +192,9 @@ const presets = {
|
||||
textShadow: true,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.3)',
|
||||
shadowBlur: 10,
|
||||
showSeconds: false
|
||||
showSeconds: false,
|
||||
showDate: true,
|
||||
dateFormat: 'YYYY-MM-DD'
|
||||
},
|
||||
neon: {
|
||||
format: 'HH:mm:ss',
|
||||
@ -179,7 +206,8 @@ const presets = {
|
||||
textShadow: true,
|
||||
shadowColor: 'rgba(57, 255, 20, 0.8)',
|
||||
shadowBlur: 15,
|
||||
showSeconds: true
|
||||
showSeconds: true,
|
||||
showDate: false
|
||||
},
|
||||
elegant: {
|
||||
format: 'hh:mm A',
|
||||
@ -191,7 +219,9 @@ const presets = {
|
||||
textShadow: true,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
shadowBlur: 5,
|
||||
showSeconds: false
|
||||
showSeconds: false,
|
||||
showDate: true,
|
||||
dateFormat: 'MMMM D, YYYY'
|
||||
},
|
||||
minimal: {
|
||||
format: 'HH:mm',
|
||||
@ -201,7 +231,8 @@ const presets = {
|
||||
color: '#ffffff',
|
||||
useGradient: false,
|
||||
textShadow: false,
|
||||
showSeconds: false
|
||||
showSeconds: false,
|
||||
showDate: false
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div class="date-config">
|
||||
<h2>Date Widget Settings</h2>
|
||||
<h2>日期小组件设置</h2>
|
||||
|
||||
<el-form label-position="top">
|
||||
<el-form-item label="Date Format">
|
||||
<el-select v-model="localConfig.format" placeholder="Select format">
|
||||
<el-form-item label="日期格式">
|
||||
<el-select v-model="localConfig.format" placeholder="选择格式">
|
||||
<el-option label="YYYY-MM-DD" value="YYYY-MM-DD" />
|
||||
<el-option label="MM/DD/YYYY" value="MM/DD/YYYY" />
|
||||
<el-option label="DD/MM/YYYY" value="DD/MM/YYYY" />
|
||||
@ -13,17 +13,17 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Show Weekday">
|
||||
<el-form-item label="显示星期">
|
||||
<el-switch v-model="localConfig.showWeekday" />
|
||||
</el-form-item>
|
||||
|
||||
<el-divider>Text Style</el-divider>
|
||||
<el-divider>文本样式</el-divider>
|
||||
|
||||
<el-form-item label="Font Size">
|
||||
<el-form-item label="字体大小">
|
||||
<el-slider v-model="localConfig.fontSize" :min="12" :max="80" show-input />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Font Family">
|
||||
<el-form-item label="字体">
|
||||
<el-select v-model="localConfig.fontFamily">
|
||||
<el-option label="Arial" value="Arial" />
|
||||
<el-option label="Helvetica" value="Helvetica" />
|
||||
@ -35,58 +35,58 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Font Weight">
|
||||
<el-form-item label="字重">
|
||||
<el-select v-model="localConfig.fontWeight">
|
||||
<el-option label="Normal" value="normal" />
|
||||
<el-option label="Bold" value="bold" />
|
||||
<el-option label="Light" value="lighter" />
|
||||
<el-option label="普通" value="normal" />
|
||||
<el-option label="粗体" value="bold" />
|
||||
<el-option label="细体" value="lighter" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-divider>Color Settings</el-divider>
|
||||
<el-divider>颜色设置</el-divider>
|
||||
|
||||
<el-form-item label="Use Gradient Colors">
|
||||
<el-form-item label="使用渐变色">
|
||||
<el-switch v-model="localConfig.useGradient" />
|
||||
</el-form-item>
|
||||
|
||||
<template v-if="!localConfig.useGradient">
|
||||
<el-form-item label="Text Color">
|
||||
<el-form-item label="文本颜色">
|
||||
<el-color-picker v-model="localConfig.color" show-alpha />
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<el-form-item label="Gradient Start Color">
|
||||
<el-form-item label="渐变起始颜色">
|
||||
<el-color-picker v-model="localConfig.gradientColors[0]" show-alpha />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Gradient End Color">
|
||||
<el-form-item label="渐变结束颜色">
|
||||
<el-color-picker v-model="localConfig.gradientColors[1]" show-alpha />
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<el-divider>Effects</el-divider>
|
||||
<el-divider>特效</el-divider>
|
||||
|
||||
<el-form-item label="Text Shadow">
|
||||
<el-form-item label="文字阴影">
|
||||
<el-switch v-model="localConfig.textShadow" />
|
||||
</el-form-item>
|
||||
|
||||
<template v-if="localConfig.textShadow">
|
||||
<el-form-item label="Shadow Color">
|
||||
<el-form-item label="阴影颜色">
|
||||
<el-color-picker v-model="localConfig.shadowColor" show-alpha />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Shadow Blur">
|
||||
<el-form-item label="阴影模糊度">
|
||||
<el-slider v-model="localConfig.shadowBlur" :min="0" :max="20" show-input />
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<el-form-item>
|
||||
<el-button-group>
|
||||
<el-button type="primary" @click="applyPreset('modern')">Modern</el-button>
|
||||
<el-button type="success" @click="applyPreset('elegant')">Elegant</el-button>
|
||||
<el-button type="warning" @click="applyPreset('casual')">Casual</el-button>
|
||||
<el-button type="danger" @click="applyPreset('minimal')">Minimal</el-button>
|
||||
<el-button type="primary" @click="applyPreset('modern')">现代风格</el-button>
|
||||
<el-button type="success" @click="applyPreset('elegant')">优雅风格</el-button>
|
||||
<el-button type="warning" @click="applyPreset('casual')">休闲风格</el-button>
|
||||
<el-button type="danger" @click="applyPreset('minimal')">简约风格</el-button>
|
||||
</el-button-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
@ -1,56 +1,56 @@
|
||||
<template>
|
||||
<div class="image-config">
|
||||
<h2>Image Widget Settings</h2>
|
||||
<h2>图片小组件设置</h2>
|
||||
|
||||
<el-form label-position="top">
|
||||
<el-form-item label="Image URL">
|
||||
<el-input v-model="localConfig.imageUrl" placeholder="Enter image URL" />
|
||||
<el-form-item label="图片URL">
|
||||
<el-input v-model="localConfig.imageUrl" placeholder="输入图片URL" />
|
||||
</el-form-item>
|
||||
|
||||
<div class="preview-image" v-if="localConfig.imageUrl">
|
||||
<img :src="localConfig.imageUrl" alt="Preview" style="max-width: 100%; max-height: 150px;" />
|
||||
<img :src="localConfig.imageUrl" alt="预览" style="max-width: 100%; max-height: 150px;" />
|
||||
</div>
|
||||
|
||||
<el-divider>Size & Appearance</el-divider>
|
||||
<el-divider>尺寸与外观</el-divider>
|
||||
|
||||
<el-form-item label="Width (px)">
|
||||
<el-form-item label="宽度 (像素)">
|
||||
<el-slider v-model="localConfig.width" :min="50" :max="800" show-input />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Height (px)">
|
||||
<el-form-item label="高度 (像素)">
|
||||
<el-slider v-model="localConfig.height" :min="50" :max="800" show-input />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Opacity">
|
||||
<el-form-item label="透明度">
|
||||
<el-slider v-model="localConfig.opacity" :min="0" :max="1" :step="0.01" show-input />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Border Radius (px)">
|
||||
<el-form-item label="圆角半径 (像素)">
|
||||
<el-slider v-model="localConfig.borderRadius" :min="0" :max="100" show-input />
|
||||
</el-form-item>
|
||||
|
||||
<el-divider>Effects</el-divider>
|
||||
<el-divider>特效</el-divider>
|
||||
|
||||
<el-form-item label="Shadow">
|
||||
<el-form-item label="阴影">
|
||||
<el-switch v-model="localConfig.shadow" />
|
||||
</el-form-item>
|
||||
|
||||
<template v-if="localConfig.shadow">
|
||||
<el-form-item label="Shadow Color">
|
||||
<el-form-item label="阴影颜色">
|
||||
<el-color-picker v-model="localConfig.shadowColor" show-alpha />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Shadow Blur">
|
||||
<el-form-item label="阴影模糊度">
|
||||
<el-slider v-model="localConfig.shadowBlur" :min="0" :max="50" show-input />
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<el-form-item>
|
||||
<el-button-group>
|
||||
<el-button type="primary" @click="applyPreset('normal')">Normal</el-button>
|
||||
<el-button type="success" @click="applyPreset('rounded')">Rounded</el-button>
|
||||
<el-button type="warning" @click="applyPreset('shadow')">Shadow</el-button>
|
||||
<el-button type="danger" @click="applyPreset('circular')">Circular</el-button>
|
||||
<el-button type="primary" @click="applyPreset('normal')">正常</el-button>
|
||||
<el-button type="success" @click="applyPreset('rounded')">圆角</el-button>
|
||||
<el-button type="warning" @click="applyPreset('shadow')">阴影</el-button>
|
||||
<el-button type="danger" @click="applyPreset('circular')">圆形</el-button>
|
||||
</el-button-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
@ -1,19 +1,19 @@
|
||||
<template>
|
||||
<div class="text-config">
|
||||
<h2>Text Widget Settings</h2>
|
||||
<h2>文本小组件设置</h2>
|
||||
|
||||
<el-form label-position="top">
|
||||
<el-form-item label="Text Content">
|
||||
<el-input v-model="localConfig.text" type="textarea" :rows="3" placeholder="Enter text to display" />
|
||||
<el-form-item label="文本内容">
|
||||
<el-input v-model="localConfig.text" type="textarea" :rows="3" placeholder="输入要显示的文本" />
|
||||
</el-form-item>
|
||||
|
||||
<el-divider>Text Style</el-divider>
|
||||
<el-divider>文本样式</el-divider>
|
||||
|
||||
<el-form-item label="Font Size">
|
||||
<el-form-item label="字体大小">
|
||||
<el-slider v-model="localConfig.fontSize" :min="12" :max="100" show-input />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Font Family">
|
||||
<el-form-item label="字体">
|
||||
<el-select v-model="localConfig.fontFamily">
|
||||
<el-option label="Arial" value="Arial" />
|
||||
<el-option label="Helvetica" value="Helvetica" />
|
||||
@ -25,58 +25,58 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Font Weight">
|
||||
<el-form-item label="字重">
|
||||
<el-select v-model="localConfig.fontWeight">
|
||||
<el-option label="Normal" value="normal" />
|
||||
<el-option label="Bold" value="bold" />
|
||||
<el-option label="Light" value="lighter" />
|
||||
<el-option label="普通" value="normal" />
|
||||
<el-option label="粗体" value="bold" />
|
||||
<el-option label="细体" value="lighter" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-divider>Color Settings</el-divider>
|
||||
<el-divider>颜色设置</el-divider>
|
||||
|
||||
<el-form-item label="Use Gradient Colors">
|
||||
<el-form-item label="使用渐变色">
|
||||
<el-switch v-model="localConfig.useGradient" />
|
||||
</el-form-item>
|
||||
|
||||
<template v-if="!localConfig.useGradient">
|
||||
<el-form-item label="Text Color">
|
||||
<el-form-item label="文本颜色">
|
||||
<el-color-picker v-model="localConfig.color" show-alpha />
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<el-form-item label="Gradient Start Color">
|
||||
<el-form-item label="渐变起始颜色">
|
||||
<el-color-picker v-model="localConfig.gradientColors[0]" show-alpha />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Gradient End Color">
|
||||
<el-form-item label="渐变结束颜色">
|
||||
<el-color-picker v-model="localConfig.gradientColors[1]" show-alpha />
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<el-divider>Effects</el-divider>
|
||||
<el-divider>特效</el-divider>
|
||||
|
||||
<el-form-item label="Text Shadow">
|
||||
<el-form-item label="文字阴影">
|
||||
<el-switch v-model="localConfig.textShadow" />
|
||||
</el-form-item>
|
||||
|
||||
<template v-if="localConfig.textShadow">
|
||||
<el-form-item label="Shadow Color">
|
||||
<el-form-item label="阴影颜色">
|
||||
<el-color-picker v-model="localConfig.shadowColor" show-alpha />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="Shadow Blur">
|
||||
<el-form-item label="阴影模糊度">
|
||||
<el-slider v-model="localConfig.shadowBlur" :min="0" :max="20" show-input />
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<el-form-item>
|
||||
<el-button-group>
|
||||
<el-button type="primary" @click="applyPreset('modern')">Modern</el-button>
|
||||
<el-button type="success" @click="applyPreset('neon')">Neon</el-button>
|
||||
<el-button type="warning" @click="applyPreset('retro')">Retro</el-button>
|
||||
<el-button type="danger" @click="applyPreset('minimal')">Minimal</el-button>
|
||||
<el-button type="primary" @click="applyPreset('modern')">现代风格</el-button>
|
||||
<el-button type="success" @click="applyPreset('neon')">霓虹风格</el-button>
|
||||
<el-button type="warning" @click="applyPreset('retro')">复古风格</el-button>
|
||||
<el-button type="danger" @click="applyPreset('minimal')">简约风格</el-button>
|
||||
</el-button-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<div class="config-view">
|
||||
<div class="left-panel">
|
||||
<div class="widget-selector">
|
||||
<el-select v-model="selectedWidget" placeholder="Select Widget" @change="handleWidgetChange">
|
||||
<el-select v-model="selectedWidget" placeholder="选择小组件" @change="handleWidgetChange">
|
||||
<el-option v-for="widget in widgets" :key="widget.value" :label="widget.label" :value="widget.value" />
|
||||
</el-select>
|
||||
</div>
|
||||
@ -15,7 +15,7 @@
|
||||
<el-input v-model="generatedUrl" readonly>
|
||||
<template #append>
|
||||
<el-button @click="copyUrl">
|
||||
<el-icon><CopyDocument /></el-icon> Copy
|
||||
<el-icon><CopyDocument /></el-icon> 复制
|
||||
</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
@ -38,42 +38,42 @@ import { CopyDocument } from '@element-plus/icons-vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { encodeConfig, decodeConfig } from '../utils/configUtils';
|
||||
|
||||
// Widget components and their configs
|
||||
// 小组件组件及其配置
|
||||
import ClockWidget from '../widgets/ClockWidget.vue';
|
||||
import DateWidget from '../widgets/DateWidget.vue';
|
||||
import TextWidget from '../widgets/TextWidget.vue';
|
||||
import ImageWidget from '../widgets/ImageWidget.vue';
|
||||
|
||||
// Config components
|
||||
// 配置组件
|
||||
import ClockConfig from '../components/config/ClockConfig.vue';
|
||||
import DateConfig from '../components/config/DateConfig.vue';
|
||||
import TextConfig from '../components/config/TextConfig.vue';
|
||||
import ImageConfig from '../components/config/ImageConfig.vue';
|
||||
|
||||
const widgets = [
|
||||
{ label: 'Clock Widget', value: 'clock', component: ClockWidget, configComponent: ClockConfig },
|
||||
{ label: 'Date Widget', value: 'date', component: DateWidget, configComponent: DateConfig },
|
||||
{ label: 'Text Widget', value: 'text', component: TextWidget, configComponent: TextConfig },
|
||||
{ label: 'Image Widget', value: 'image', component: ImageWidget, configComponent: ImageConfig },
|
||||
{ label: '时钟小组件', value: 'clock', component: ClockWidget, configComponent: ClockConfig },
|
||||
{ label: '日期小组件', value: 'date', component: DateWidget, configComponent: DateConfig },
|
||||
{ label: '文本小组件', value: 'text', component: TextWidget, configComponent: TextConfig },
|
||||
{ label: '图片小组件', value: 'image', component: ImageWidget, configComponent: ImageConfig },
|
||||
];
|
||||
|
||||
const selectedWidget = ref('clock');
|
||||
const currentWidgetConfig = ref({});
|
||||
const generatedUrl = ref('');
|
||||
|
||||
// Get widget component based on selection
|
||||
// 根据选择获取小组件组件
|
||||
const currentWidgetComponent = computed(() => {
|
||||
const widget = widgets.find(w => w.value === selectedWidget.value);
|
||||
return widget?.component;
|
||||
});
|
||||
|
||||
// Get config component based on selection
|
||||
// 根据选择获取配置组件
|
||||
const currentConfigComponent = computed(() => {
|
||||
const widget = widgets.find(w => w.value === selectedWidget.value);
|
||||
return widget?.configComponent;
|
||||
});
|
||||
|
||||
// Set default config for each widget type
|
||||
// 为每种小组件类型设置默认配置
|
||||
const getDefaultConfig = (widgetType: string) => {
|
||||
switch (widgetType) {
|
||||
case 'clock':
|
||||
@ -139,13 +139,13 @@ const handleWidgetChange = () => {
|
||||
updateGeneratedUrl();
|
||||
};
|
||||
|
||||
// Update widget configuration
|
||||
// 更新小组件配置
|
||||
const updateWidgetConfig = (newConfig: any) => {
|
||||
currentWidgetConfig.value = newConfig;
|
||||
updateGeneratedUrl();
|
||||
};
|
||||
|
||||
// Generate preview URL
|
||||
// 生成预览 URL
|
||||
const updateGeneratedUrl = () => {
|
||||
const baseUrl = window.location.origin;
|
||||
const configStr = encodeConfig({
|
||||
@ -155,16 +155,16 @@ const updateGeneratedUrl = () => {
|
||||
generatedUrl.value = `${baseUrl}/preview?data=${configStr}`;
|
||||
};
|
||||
|
||||
// Copy URL to clipboard
|
||||
// 复制 URL 到剪贴板
|
||||
const copyUrl = () => {
|
||||
navigator.clipboard.writeText(generatedUrl.value).then(() => {
|
||||
ElMessage.success('URL copied to clipboard!');
|
||||
ElMessage.success('URL 已复制到剪贴板!');
|
||||
}).catch(() => {
|
||||
ElMessage.error('Failed to copy URL');
|
||||
ElMessage.error('复制 URL 失败');
|
||||
});
|
||||
};
|
||||
|
||||
// Check for query params on load (for direct linking)
|
||||
// 加载时检查查询参数(用于直接链接)
|
||||
onMounted(() => {
|
||||
const queryParams = new URLSearchParams(window.location.search);
|
||||
const data = queryParams.get('data');
|
||||
@ -175,15 +175,15 @@ onMounted(() => {
|
||||
selectedWidget.value = decodedData.type;
|
||||
currentWidgetConfig.value = decodedData.config;
|
||||
} catch (e) {
|
||||
ElMessage.error('Invalid configuration in URL');
|
||||
handleWidgetChange(); // Load default config
|
||||
ElMessage.error('URL 中的配置无效');
|
||||
handleWidgetChange(); // 加载默认配置
|
||||
}
|
||||
} else {
|
||||
handleWidgetChange(); // Load default config
|
||||
handleWidgetChange(); // 加载默认配置
|
||||
}
|
||||
});
|
||||
|
||||
// Update URL when configuration changes
|
||||
// 配置变更时更新 URL
|
||||
watch([selectedWidget, currentWidgetConfig], () => {
|
||||
updateGeneratedUrl();
|
||||
}, { deep: true });
|
||||
|
@ -2,8 +2,8 @@
|
||||
<div class="home-view">
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>OBS Overlay Widget</h1>
|
||||
<p>Create customizable widgets for OBS Studio streaming and recording</p>
|
||||
<h1>OBS 悬浮小组件</h1>
|
||||
<p>为 OBS Studio 直播和录制场景创建可定制化小组件</p>
|
||||
</div>
|
||||
|
||||
<div class="cards">
|
||||
@ -11,9 +11,9 @@
|
||||
<div class="card-icon">
|
||||
<el-icon><Setting /></el-icon>
|
||||
</div>
|
||||
<div class="card-title">Configure Widgets</div>
|
||||
<div class="card-title">配置小组件</div>
|
||||
<div class="card-description">
|
||||
Design and customize widgets for your OBS streams with an interactive interface
|
||||
通过交互式界面设计和自定义 OBS 直播小组件
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -21,53 +21,53 @@
|
||||
<div class="card-icon">
|
||||
<el-icon><Document /></el-icon>
|
||||
</div>
|
||||
<div class="card-title">Documentation</div>
|
||||
<div class="card-title">使用文档</div>
|
||||
<div class="card-description">
|
||||
Learn how to use and integrate OBS Overlay Widgets into your streams
|
||||
了解如何使用和集成 OBS 悬浮小组件到您的直播中
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="features">
|
||||
<h2>Available Widgets</h2>
|
||||
<h2>可用小组件</h2>
|
||||
|
||||
<div class="widget-list">
|
||||
<div class="widget-item">
|
||||
<div class="widget-icon">⏰</div>
|
||||
<div class="widget-info">
|
||||
<h3>Clock Widget</h3>
|
||||
<p>Display current time with customizable format, style, and effects</p>
|
||||
<h3>时钟小组件</h3>
|
||||
<p>显示当前时间,可自定义格式、样式和特效</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="widget-item">
|
||||
<div class="widget-icon">📅</div>
|
||||
<div class="widget-info">
|
||||
<h3>Date Widget</h3>
|
||||
<p>Show current date with customizable format, style, and effects</p>
|
||||
<h3>日期小组件</h3>
|
||||
<p>显示当前日期,可自定义格式、样式和特效</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="widget-item">
|
||||
<div class="widget-icon">📝</div>
|
||||
<div class="widget-info">
|
||||
<h3>Text Widget</h3>
|
||||
<p>Display text with customizable styles including gradients, shadows, and fonts</p>
|
||||
<h3>文本小组件</h3>
|
||||
<p>显示文本,支持渐变、阴影、字体等自定义样式</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="widget-item">
|
||||
<div class="widget-icon">🖼️</div>
|
||||
<div class="widget-info">
|
||||
<h3>Image Widget</h3>
|
||||
<p>Show images with customizable size, effects, and positioning</p>
|
||||
<h3>图片小组件</h3>
|
||||
<p>显示图片,可自定义大小、特效和位置</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<p>OBS Overlay Widget © 2025</p>
|
||||
<p>OBS 悬浮小组件 © 2025</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -84,7 +84,7 @@ const goToConfig = () => {
|
||||
};
|
||||
|
||||
const goToDoc = () => {
|
||||
// This would go to documentation in a real app
|
||||
// 在实际应用中,这里会跳转到文档页面
|
||||
window.open('https://github.com/yourusername/obs-overlay-widget', '_blank');
|
||||
};
|
||||
</script>
|
||||
|
@ -12,13 +12,13 @@
|
||||
import { ref, onMounted, computed } from 'vue';
|
||||
import { decodeConfig } from '../utils/configUtils';
|
||||
|
||||
// Import widget components
|
||||
// 导入小组件组件
|
||||
import ClockWidget from '../widgets/ClockWidget.vue';
|
||||
import DateWidget from '../widgets/DateWidget.vue';
|
||||
import TextWidget from '../widgets/TextWidget.vue';
|
||||
import ImageWidget from '../widgets/ImageWidget.vue';
|
||||
|
||||
// Widget registry
|
||||
// 小组件注册表
|
||||
const widgetRegistry = {
|
||||
'clock': ClockWidget,
|
||||
'date': DateWidget,
|
||||
@ -42,7 +42,7 @@ onMounted(() => {
|
||||
widgetType.value = decodedData.type;
|
||||
widgetConfig.value = decodedData.config;
|
||||
} catch (e) {
|
||||
console.error('Invalid configuration in URL', e);
|
||||
console.error('URL 中的配置无效', e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -1,6 +1,7 @@
|
||||
<template>
|
||||
<div class="clock-widget" :style="clockStyle">
|
||||
{{ currentTime }}
|
||||
<div>{{ currentTime }}</div>
|
||||
<div v-if="props.config.showDate" :style="clockStyle">{{ currentDate }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -21,6 +22,8 @@ interface ClockConfig {
|
||||
useGradient: boolean;
|
||||
gradientColors: string[];
|
||||
showSeconds: boolean;
|
||||
showDate: boolean;
|
||||
dateFormat: string;
|
||||
}
|
||||
|
||||
// Define props with default values
|
||||
@ -38,18 +41,27 @@ const props = withDefaults(defineProps<{
|
||||
shadowBlur: 4,
|
||||
useGradient: false,
|
||||
gradientColors: ['#ff0000', '#0000ff'],
|
||||
showSeconds: true
|
||||
showSeconds: true,
|
||||
showDate: false,
|
||||
dateFormat: 'YYYY-MM-DD'
|
||||
})
|
||||
});
|
||||
|
||||
// State for current time
|
||||
// State for current time and date
|
||||
const currentTime = ref('');
|
||||
const currentDate = ref('');
|
||||
|
||||
// Update time string based on format
|
||||
const updateTime = () => {
|
||||
const currentFormat = props.config.format || 'HH:mm:ss';
|
||||
const format = props.config.showSeconds ? currentFormat : currentFormat.replace(':ss', '');
|
||||
currentTime.value = dayjs().format(format);
|
||||
|
||||
// Update date if enabled
|
||||
if (props.config.showDate) {
|
||||
const dateFormat = props.config.dateFormat || 'YYYY-MM-DD';
|
||||
currentDate.value = dayjs().format(dateFormat);
|
||||
}
|
||||
};
|
||||
|
||||
// Interval for updating time
|
||||
@ -71,7 +83,7 @@ onUnmounted(() => {
|
||||
}
|
||||
});
|
||||
|
||||
// Update interval if showSeconds changes
|
||||
// Watch for showSeconds changes
|
||||
watch(() => props.config.showSeconds, (newValue) => {
|
||||
if (timeInterval !== null) {
|
||||
window.clearInterval(timeInterval);
|
||||
@ -82,6 +94,11 @@ watch(() => props.config.showSeconds, (newValue) => {
|
||||
updateTime();
|
||||
});
|
||||
|
||||
// Watch for showDate or dateFormat changes
|
||||
watch([() => props.config.showDate, () => props.config.dateFormat], () => {
|
||||
updateTime();
|
||||
}, { deep: true });
|
||||
|
||||
// Computed styles for the clock
|
||||
const clockStyle = computed(() => {
|
||||
const style: Record<string, string> = {
|
||||
@ -116,5 +133,6 @@ const clockStyle = computed(() => {
|
||||
padding: 10px;
|
||||
font-family: Arial, sans-serif;
|
||||
user-select: none;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
Loading…
x
Reference in New Issue
Block a user