video_default.html 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="template:media-width" content="512">
  6. <meta name="template:media-height" content="288">
  7. <style>
  8. html {
  9. margin: 0;
  10. padding: 0;
  11. height: 100%;
  12. }
  13. body {
  14. margin: 0;
  15. padding: 0;
  16. width: 100%;
  17. height: 100vh;
  18. font-family: 'PingFang SC', 'Source Han Sans', 'Microsoft YaHei', sans-serif;
  19. overflow: hidden;
  20. /* background-color: #000; */
  21. display: flex;
  22. justify-content: center;
  23. align-items: center;
  24. }
  25. /* 主容器 - 居中并包含所有内容 */
  26. .main-container {
  27. position: relative;
  28. width: 1080px;
  29. height: 1920px;
  30. }
  31. /* Background image layer (customizable using <img> tag) */
  32. .background-image {
  33. position: absolute;
  34. top: 0;
  35. left: 0;
  36. width: 100%;
  37. height: 100%;
  38. z-index: 0;
  39. }
  40. /* Video overlay - 相对于main-container居中 */
  41. .video-overlay {
  42. position: absolute;
  43. top: 50%;
  44. left: 50%;
  45. transform: translate(-50%, -50%);
  46. width: 1080px;
  47. height: 607px;
  48. /* background: #f00; */
  49. z-index: 1;
  50. }
  51. /* Title section - positioned above video */
  52. .video-title-wrapper {
  53. position: absolute;
  54. top: calc(50% - 607px / 2 - 130px);
  55. left: 50%;
  56. transform: translateX(-50%);
  57. z-index: 2;
  58. }
  59. .video-title {
  60. width: 900px;
  61. text-align: center;
  62. /* 初始字体大小,JavaScript 会根据文本长度自动调整 */
  63. font-size: 72px;
  64. font-weight: 700;
  65. color: #ffffff;
  66. line-height: 1.3;
  67. letter-spacing: 3px;
  68. text-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
  69. margin-bottom: 20px;
  70. /* 确保文本不会溢出 */
  71. word-wrap: break-word;
  72. overflow-wrap: break-word;
  73. white-space: nowrap;
  74. }
  75. /* 字幕区域 - 对齐视频底部 */
  76. .content {
  77. position: absolute;
  78. bottom: calc(50% - 607px / 2 + 0px);
  79. left: 50%;
  80. transform: translateX(-50%);
  81. width: 900px;
  82. z-index: 4;
  83. }
  84. .text {
  85. font-size: 40px;
  86. color: #ffffff;
  87. text-align: center;
  88. line-height: 1.6;
  89. font-weight: 500;
  90. text-shadow:
  91. 2px 2px 4px rgba(0, 0, 0, 0.9),
  92. 0 0 8px rgba(0, 0, 0, 0.8),
  93. 0 0 16px rgba(0, 0, 0, 0.6);
  94. padding: 10px 0px;
  95. /* background-color: aqua; */
  96. }
  97. /* Footer - positioned below video */
  98. .footer {
  99. position: absolute;
  100. top: calc(50% + 607px / 2 + 50px);
  101. left: 50%;
  102. transform: translateX(-50%);
  103. width: 900px;
  104. display: flex;
  105. align-items: center;
  106. justify-content: space-between;
  107. padding-top: 40px;
  108. border-top: 2px solid rgba(255, 255, 255, 0.3);
  109. z-index: 2;
  110. }
  111. .author-section {
  112. display: flex;
  113. flex-direction: column;
  114. gap: 8px;
  115. }
  116. .author {
  117. font-size: 32px;
  118. font-weight: 600;
  119. color: #ffffff;
  120. text-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
  121. }
  122. .author-desc {
  123. font-size: 24px;
  124. color: rgba(255, 255, 255, 0.9);
  125. font-weight: 400;
  126. }
  127. .logo-section {
  128. display: flex;
  129. flex-direction: column;
  130. align-items: flex-end;
  131. gap: 10px;
  132. }
  133. .logo {
  134. font-size: 28px;
  135. font-weight: 600;
  136. color: #ffffff;
  137. letter-spacing: 2px;
  138. text-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
  139. }
  140. </style>
  141. </head>
  142. <body>
  143. <!-- 主容器 - 所有元素都在这里面,相对于video-overlay定位 -->
  144. <div class="main-container">
  145. <!-- Background image layer (customizable via background parameter) -->
  146. <div class="background-image">
  147. </div>
  148. <!-- Video overlay - 居中参考点 -->
  149. <div class="video-overlay"></div>
  150. <!-- Video title - positioned above video -->
  151. <div class="video-title-wrapper">
  152. <div class="video-title">{{title}}</div>
  153. </div>
  154. <!-- 字幕区域 - 独立定位在视频底部 -->
  155. <div class="content">
  156. <div class="text">{{text}}</div>
  157. </div>
  158. <!-- Footer - positioned below video -->
  159. <div class="footer">
  160. <div class="author-section">
  161. <div class="author">{{author=@Pixelle.AI}}</div>
  162. <div class="author-desc">{{describe=Open Source Omnimodal AI Creative Agent}}</div>
  163. </div>
  164. <div class="logo-section">
  165. <div class="logo">{{brand=Pixelle-Video}}</div>
  166. </div>
  167. </div>
  168. </div>
  169. <script>
  170. /**
  171. * 根据文本宽度自动调整字体大小
  172. * @param {HTMLElement} element - 要调整的元素
  173. * @param {number} maxWidth - 最大宽度(px)
  174. * @param {number} minFontSize - 最小字体大小(px)
  175. * @param {number} maxFontSize - 最大字体大小(px)
  176. */
  177. function autoFitFontSize(element, maxWidth, minFontSize, maxFontSize) {
  178. try {
  179. if (!element || !element.textContent || !element.textContent.trim()) {
  180. return;
  181. }
  182. if (!document.body) {
  183. return;
  184. }
  185. // 创建一个临时的测量元素
  186. const measure = document.createElement('span');
  187. measure.style.visibility = 'hidden';
  188. measure.style.position = 'absolute';
  189. measure.style.top = '-9999px';
  190. measure.style.left = '-9999px';
  191. measure.style.whiteSpace = 'nowrap';
  192. // 复制元素的样式
  193. const computedStyle = window.getComputedStyle(element);
  194. measure.style.fontFamily = computedStyle.fontFamily;
  195. measure.style.fontSize = computedStyle.fontSize;
  196. measure.style.fontWeight = computedStyle.fontWeight;
  197. measure.style.letterSpacing = computedStyle.letterSpacing;
  198. measure.textContent = element.textContent;
  199. document.body.appendChild(measure);
  200. // 二分查找合适的字体大小
  201. let low = minFontSize;
  202. let high = maxFontSize;
  203. let bestSize = maxFontSize;
  204. // 先检查最大字体是否合适
  205. measure.style.fontSize = maxFontSize + 'px';
  206. if (measure.offsetWidth <= maxWidth) {
  207. bestSize = maxFontSize;
  208. } else {
  209. // 使用二分查找
  210. while (high - low > 0.5) {
  211. const mid = (low + high) / 2;
  212. measure.style.fontSize = mid + 'px';
  213. if (measure.offsetWidth <= maxWidth) {
  214. bestSize = mid;
  215. low = mid;
  216. } else {
  217. high = mid;
  218. }
  219. }
  220. }
  221. // 应用找到的字体大小
  222. element.style.fontSize = bestSize + 'px';
  223. // 清理临时元素
  224. if (measure.parentNode) {
  225. document.body.removeChild(measure);
  226. }
  227. } catch (error) {
  228. console.error('自动调整字体大小出错:', error);
  229. }
  230. }
  231. // 页面加载完成后自动调整字体大小
  232. function initAutoFit() {
  233. try {
  234. const titleElement = document.querySelector('.video-title');
  235. if (titleElement) {
  236. // 获取容器的实际宽度
  237. const wrapper = document.querySelector('.video-title-wrapper');
  238. const maxWidth = wrapper ? wrapper.offsetWidth : 900;
  239. // 自动调整字体大小:最小12px,最大72px
  240. autoFitFontSize(titleElement, maxWidth, 12, 72);
  241. }
  242. } catch (error) {
  243. console.error('初始化自动调整字体大小出错:', error);
  244. }
  245. }
  246. // 等待 DOM 加载完成
  247. if (document.readyState === 'loading') {
  248. document.addEventListener('DOMContentLoaded', initAutoFit);
  249. } else {
  250. // DOM 已经加载完成
  251. initAutoFit();
  252. }
  253. // 如果内容是通过模板引擎动态插入的,可以监听内容变化
  254. // 使用 MutationObserver 监听文本变化
  255. if (typeof MutationObserver !== 'undefined') {
  256. try {
  257. const observer = new MutationObserver(function(mutations) {
  258. let shouldUpdate = false;
  259. mutations.forEach(function(mutation) {
  260. if (mutation.type === 'childList' || mutation.type === 'characterData') {
  261. shouldUpdate = true;
  262. }
  263. });
  264. if (shouldUpdate) {
  265. // 延迟执行,避免频繁调用
  266. setTimeout(initAutoFit, 100);
  267. }
  268. });
  269. // 等待 body 元素存在后再观察
  270. if (document.body) {
  271. observer.observe(document.body, {
  272. childList: true,
  273. subtree: true,
  274. characterData: true
  275. });
  276. } else {
  277. document.addEventListener('DOMContentLoaded', function() {
  278. if (document.body) {
  279. observer.observe(document.body, {
  280. childList: true,
  281. subtree: true,
  282. characterData: true
  283. });
  284. }
  285. });
  286. }
  287. } catch (error) {
  288. console.error('设置 MutationObserver 出错:', error);
  289. }
  290. }
  291. </script>
  292. </body>
  293. </html>