@jjlmoya/utils-audiovisual 1.17.0 → 1.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/category/index.ts +4 -0
- package/src/entries.ts +7 -1
- package/src/index.ts +2 -0
- package/src/pages/[locale]/[slug].astro +28 -12
- package/src/tests/locale_completeness.test.ts +2 -36
- package/src/tests/shared-test-helpers.ts +56 -0
- package/src/tests/tool_exports.test.ts +34 -0
- package/src/tests/tool_validation.test.ts +2 -2
- package/src/tool/chromaticLens/bibliography.ts +12 -0
- package/src/tool/chromaticLens/entry.ts +2 -0
- package/src/tool/chromaticLens/i18n/de.ts +1 -15
- package/src/tool/chromaticLens/i18n/en.ts +1 -15
- package/src/tool/chromaticLens/i18n/es.ts +1 -13
- package/src/tool/chromaticLens/i18n/fr.ts +1 -13
- package/src/tool/chromaticLens/i18n/id.ts +1 -15
- package/src/tool/chromaticLens/i18n/it.ts +1 -15
- package/src/tool/chromaticLens/i18n/ja.ts +1 -15
- package/src/tool/chromaticLens/i18n/ko.ts +1 -15
- package/src/tool/chromaticLens/i18n/nl.ts +1 -15
- package/src/tool/chromaticLens/i18n/pl.ts +1 -15
- package/src/tool/chromaticLens/i18n/pt.ts +1 -15
- package/src/tool/chromaticLens/i18n/ru.ts +1 -15
- package/src/tool/chromaticLens/i18n/sv.ts +1 -15
- package/src/tool/chromaticLens/i18n/tr.ts +1 -15
- package/src/tool/chromaticLens/i18n/zh.ts +1 -15
- package/src/tool/chromaticLens/seo.astro +1 -1
- package/src/tool/collageMaker/bibliography.ts +8 -0
- package/src/tool/collageMaker/entry.ts +2 -0
- package/src/tool/collageMaker/i18n/de.ts +1 -11
- package/src/tool/collageMaker/i18n/en.ts +1 -11
- package/src/tool/collageMaker/i18n/es.ts +1 -9
- package/src/tool/collageMaker/i18n/fr.ts +1 -9
- package/src/tool/collageMaker/i18n/id.ts +1 -11
- package/src/tool/collageMaker/i18n/it.ts +1 -11
- package/src/tool/collageMaker/i18n/ja.ts +1 -11
- package/src/tool/collageMaker/i18n/ko.ts +1 -11
- package/src/tool/collageMaker/i18n/nl.ts +1 -11
- package/src/tool/collageMaker/i18n/pl.ts +1 -11
- package/src/tool/collageMaker/i18n/pt.ts +1 -11
- package/src/tool/collageMaker/i18n/ru.ts +1 -11
- package/src/tool/collageMaker/i18n/sv.ts +1 -11
- package/src/tool/collageMaker/i18n/tr.ts +1 -11
- package/src/tool/collageMaker/i18n/zh.ts +1 -11
- package/src/tool/collageMaker/seo.astro +1 -1
- package/src/tool/depthOfFieldCalculator/bibliography.astro +15 -0
- package/src/tool/depthOfFieldCalculator/bibliography.ts +20 -0
- package/src/tool/depthOfFieldCalculator/component.astro +341 -0
- package/src/tool/depthOfFieldCalculator/depth-of-field-calculator.css +417 -0
- package/src/tool/depthOfFieldCalculator/entry.ts +52 -0
- package/src/tool/depthOfFieldCalculator/i18n/de.ts +141 -0
- package/src/tool/depthOfFieldCalculator/i18n/en.ts +141 -0
- package/src/tool/depthOfFieldCalculator/i18n/es.ts +141 -0
- package/src/tool/depthOfFieldCalculator/i18n/fr.ts +141 -0
- package/src/tool/depthOfFieldCalculator/i18n/id.ts +141 -0
- package/src/tool/depthOfFieldCalculator/i18n/it.ts +141 -0
- package/src/tool/depthOfFieldCalculator/i18n/ja.ts +141 -0
- package/src/tool/depthOfFieldCalculator/i18n/ko.ts +141 -0
- package/src/tool/depthOfFieldCalculator/i18n/nl.ts +141 -0
- package/src/tool/depthOfFieldCalculator/i18n/pl.ts +141 -0
- package/src/tool/depthOfFieldCalculator/i18n/pt.ts +141 -0
- package/src/tool/depthOfFieldCalculator/i18n/ru.ts +141 -0
- package/src/tool/depthOfFieldCalculator/i18n/sv.ts +141 -0
- package/src/tool/depthOfFieldCalculator/i18n/tr.ts +141 -0
- package/src/tool/depthOfFieldCalculator/i18n/zh.ts +141 -0
- package/src/tool/depthOfFieldCalculator/index.ts +10 -0
- package/src/tool/depthOfFieldCalculator/logic.ts +91 -0
- package/src/tool/depthOfFieldCalculator/seo.astro +15 -0
- package/src/tool/exifCleaner/bibliography.ts +12 -0
- package/src/tool/exifCleaner/entry.ts +2 -0
- package/src/tool/exifCleaner/i18n/de.ts +1 -15
- package/src/tool/exifCleaner/i18n/en.ts +1 -15
- package/src/tool/exifCleaner/i18n/es.ts +1 -15
- package/src/tool/exifCleaner/i18n/fr.ts +1 -15
- package/src/tool/exifCleaner/i18n/id.ts +1 -15
- package/src/tool/exifCleaner/i18n/it.ts +1 -15
- package/src/tool/exifCleaner/i18n/ja.ts +1 -15
- package/src/tool/exifCleaner/i18n/ko.ts +1 -15
- package/src/tool/exifCleaner/i18n/nl.ts +1 -15
- package/src/tool/exifCleaner/i18n/pl.ts +1 -15
- package/src/tool/exifCleaner/i18n/pt.ts +1 -15
- package/src/tool/exifCleaner/i18n/ru.ts +1 -15
- package/src/tool/exifCleaner/i18n/sv.ts +1 -15
- package/src/tool/exifCleaner/i18n/tr.ts +1 -15
- package/src/tool/exifCleaner/i18n/zh.ts +1 -15
- package/src/tool/exifCleaner/seo.astro +2 -5
- package/src/tool/imageCompressor/bibliography.ts +12 -0
- package/src/tool/imageCompressor/entry.ts +2 -0
- package/src/tool/imageCompressor/i18n/de.ts +1 -15
- package/src/tool/imageCompressor/i18n/en.ts +1 -15
- package/src/tool/imageCompressor/i18n/es.ts +1 -13
- package/src/tool/imageCompressor/i18n/fr.ts +1 -13
- package/src/tool/imageCompressor/i18n/id.ts +1 -15
- package/src/tool/imageCompressor/i18n/it.ts +1 -15
- package/src/tool/imageCompressor/i18n/ja.ts +1 -15
- package/src/tool/imageCompressor/i18n/ko.ts +1 -15
- package/src/tool/imageCompressor/i18n/nl.ts +1 -15
- package/src/tool/imageCompressor/i18n/pl.ts +1 -15
- package/src/tool/imageCompressor/i18n/pt.ts +1 -15
- package/src/tool/imageCompressor/i18n/ru.ts +1 -15
- package/src/tool/imageCompressor/i18n/sv.ts +1 -15
- package/src/tool/imageCompressor/i18n/tr.ts +1 -15
- package/src/tool/imageCompressor/i18n/zh.ts +1 -15
- package/src/tool/imageCompressor/seo.astro +1 -1
- package/src/tool/printQualityCalculator/bibliography.ts +12 -0
- package/src/tool/printQualityCalculator/entry.ts +2 -0
- package/src/tool/printQualityCalculator/i18n/de.ts +1 -15
- package/src/tool/printQualityCalculator/i18n/en.ts +1 -15
- package/src/tool/printQualityCalculator/i18n/es.ts +1 -13
- package/src/tool/printQualityCalculator/i18n/fr.ts +1 -13
- package/src/tool/printQualityCalculator/i18n/id.ts +1 -15
- package/src/tool/printQualityCalculator/i18n/it.ts +1 -15
- package/src/tool/printQualityCalculator/i18n/ja.ts +1 -15
- package/src/tool/printQualityCalculator/i18n/ko.ts +1 -15
- package/src/tool/printQualityCalculator/i18n/nl.ts +1 -15
- package/src/tool/printQualityCalculator/i18n/pl.ts +1 -15
- package/src/tool/printQualityCalculator/i18n/pt.ts +1 -15
- package/src/tool/printQualityCalculator/i18n/ru.ts +1 -15
- package/src/tool/printQualityCalculator/i18n/sv.ts +1 -15
- package/src/tool/printQualityCalculator/i18n/tr.ts +1 -15
- package/src/tool/printQualityCalculator/i18n/zh.ts +1 -15
- package/src/tool/printQualityCalculator/seo.astro +2 -5
- package/src/tool/privacyBlur/bibliography.ts +8 -0
- package/src/tool/privacyBlur/entry.ts +2 -0
- package/src/tool/privacyBlur/i18n/de.ts +1 -11
- package/src/tool/privacyBlur/i18n/en.ts +1 -11
- package/src/tool/privacyBlur/i18n/es.ts +1 -9
- package/src/tool/privacyBlur/i18n/fr.ts +1 -9
- package/src/tool/privacyBlur/i18n/id.ts +1 -11
- package/src/tool/privacyBlur/i18n/it.ts +1 -11
- package/src/tool/privacyBlur/i18n/ja.ts +1 -11
- package/src/tool/privacyBlur/i18n/ko.ts +1 -11
- package/src/tool/privacyBlur/i18n/nl.ts +1 -11
- package/src/tool/privacyBlur/i18n/pl.ts +1 -11
- package/src/tool/privacyBlur/i18n/pt.ts +1 -11
- package/src/tool/privacyBlur/i18n/ru.ts +1 -11
- package/src/tool/privacyBlur/i18n/sv.ts +1 -11
- package/src/tool/privacyBlur/i18n/tr.ts +1 -11
- package/src/tool/privacyBlur/i18n/zh.ts +1 -11
- package/src/tool/privacyBlur/seo.astro +1 -1
- package/src/tool/subtitleSync/bibliography.ts +12 -0
- package/src/tool/subtitleSync/entry.ts +2 -0
- package/src/tool/subtitleSync/i18n/de.ts +1 -13
- package/src/tool/subtitleSync/i18n/en.ts +1 -13
- package/src/tool/subtitleSync/i18n/es.ts +1 -13
- package/src/tool/subtitleSync/i18n/fr.ts +1 -13
- package/src/tool/subtitleSync/i18n/id.ts +1 -13
- package/src/tool/subtitleSync/i18n/it.ts +1 -13
- package/src/tool/subtitleSync/i18n/ja.ts +1 -13
- package/src/tool/subtitleSync/i18n/ko.ts +1 -13
- package/src/tool/subtitleSync/i18n/nl.ts +1 -13
- package/src/tool/subtitleSync/i18n/pl.ts +1 -13
- package/src/tool/subtitleSync/i18n/pt.ts +1 -13
- package/src/tool/subtitleSync/i18n/ru.ts +1 -13
- package/src/tool/subtitleSync/i18n/sv.ts +1 -13
- package/src/tool/subtitleSync/i18n/tr.ts +1 -13
- package/src/tool/subtitleSync/i18n/zh.ts +1 -13
- package/src/tool/subtitleSync/seo.astro +1 -1
- package/src/tool/timelapseCalculator/bibliography.ts +20 -0
- package/src/tool/timelapseCalculator/entry.ts +2 -0
- package/src/tool/timelapseCalculator/i18n/de.ts +1 -21
- package/src/tool/timelapseCalculator/i18n/en.ts +1 -21
- package/src/tool/timelapseCalculator/i18n/es.ts +1 -21
- package/src/tool/timelapseCalculator/i18n/fr.ts +1 -21
- package/src/tool/timelapseCalculator/i18n/id.ts +1 -21
- package/src/tool/timelapseCalculator/i18n/it.ts +1 -21
- package/src/tool/timelapseCalculator/i18n/ja.ts +1 -21
- package/src/tool/timelapseCalculator/i18n/ko.ts +1 -21
- package/src/tool/timelapseCalculator/i18n/nl.ts +1 -21
- package/src/tool/timelapseCalculator/i18n/pl.ts +1 -21
- package/src/tool/timelapseCalculator/i18n/pt.ts +1 -21
- package/src/tool/timelapseCalculator/i18n/ru.ts +1 -21
- package/src/tool/timelapseCalculator/i18n/sv.ts +1 -21
- package/src/tool/timelapseCalculator/i18n/tr.ts +1 -21
- package/src/tool/timelapseCalculator/i18n/zh.ts +1 -21
- package/src/tool/timelapseCalculator/seo.astro +2 -5
- package/src/tool/tvDistance/bibliography.ts +12 -0
- package/src/tool/tvDistance/entry.ts +2 -0
- package/src/tool/tvDistance/i18n/de.ts +1 -13
- package/src/tool/tvDistance/i18n/en.ts +1 -13
- package/src/tool/tvDistance/i18n/es.ts +1 -13
- package/src/tool/tvDistance/i18n/fr.ts +1 -13
- package/src/tool/tvDistance/i18n/id.ts +1 -13
- package/src/tool/tvDistance/i18n/it.ts +1 -13
- package/src/tool/tvDistance/i18n/ja.ts +1 -13
- package/src/tool/tvDistance/i18n/ko.ts +1 -13
- package/src/tool/tvDistance/i18n/nl.ts +1 -13
- package/src/tool/tvDistance/i18n/pl.ts +1 -13
- package/src/tool/tvDistance/i18n/pt.ts +1 -13
- package/src/tool/tvDistance/i18n/ru.ts +1 -13
- package/src/tool/tvDistance/i18n/sv.ts +1 -13
- package/src/tool/tvDistance/i18n/tr.ts +1 -13
- package/src/tool/tvDistance/i18n/zh.ts +1 -13
- package/src/tool/tvDistance/seo.astro +1 -1
- package/src/tool/videoFrameExtractor/bibliography.ts +8 -0
- package/src/tool/videoFrameExtractor/entry.ts +2 -0
- package/src/tool/videoFrameExtractor/i18n/de.ts +1 -9
- package/src/tool/videoFrameExtractor/i18n/en.ts +1 -9
- package/src/tool/videoFrameExtractor/i18n/es.ts +1 -9
- package/src/tool/videoFrameExtractor/i18n/fr.ts +1 -8
- package/src/tool/videoFrameExtractor/i18n/id.ts +1 -9
- package/src/tool/videoFrameExtractor/i18n/it.ts +1 -8
- package/src/tool/videoFrameExtractor/i18n/ja.ts +1 -9
- package/src/tool/videoFrameExtractor/i18n/ko.ts +1 -9
- package/src/tool/videoFrameExtractor/i18n/nl.ts +1 -9
- package/src/tool/videoFrameExtractor/i18n/pl.ts +1 -9
- package/src/tool/videoFrameExtractor/i18n/pt.ts +1 -9
- package/src/tool/videoFrameExtractor/i18n/ru.ts +1 -9
- package/src/tool/videoFrameExtractor/i18n/sv.ts +1 -9
- package/src/tool/videoFrameExtractor/i18n/tr.ts +1 -9
- package/src/tool/videoFrameExtractor/i18n/zh.ts +1 -9
- package/src/tool/videoFrameExtractor/seo.astro +1 -1
- package/src/tool/videoMerger/bibliography.astro +17 -0
- package/src/tool/videoMerger/bibliography.ts +16 -0
- package/src/tool/videoMerger/component.astro +400 -0
- package/src/tool/videoMerger/entry.ts +51 -0
- package/src/tool/videoMerger/i18n/de.ts +205 -0
- package/src/tool/videoMerger/i18n/en.ts +205 -0
- package/src/tool/videoMerger/i18n/es.ts +205 -0
- package/src/tool/videoMerger/i18n/fr.ts +205 -0
- package/src/tool/videoMerger/i18n/id.ts +205 -0
- package/src/tool/videoMerger/i18n/it.ts +205 -0
- package/src/tool/videoMerger/i18n/ja.ts +205 -0
- package/src/tool/videoMerger/i18n/ko.ts +205 -0
- package/src/tool/videoMerger/i18n/nl.ts +205 -0
- package/src/tool/videoMerger/i18n/pl.ts +205 -0
- package/src/tool/videoMerger/i18n/pt.ts +205 -0
- package/src/tool/videoMerger/i18n/ru.ts +205 -0
- package/src/tool/videoMerger/i18n/sv.ts +205 -0
- package/src/tool/videoMerger/i18n/tr.ts +205 -0
- package/src/tool/videoMerger/i18n/zh.ts +205 -0
- package/src/tool/videoMerger/index.ts +11 -0
- package/src/tool/videoMerger/logic.ts +263 -0
- package/src/tool/videoMerger/online-video-merger.css +440 -0
- package/src/tool/videoMerger/seo.astro +15 -0
- package/src/tools.ts +4 -0
- package/src/types.ts +0 -2
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { bibliography } from '../bibliography';
|
|
1
2
|
import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
|
|
2
3
|
import type { CollageMakerUI, CollageMakerLocaleContent } from '../index';
|
|
3
4
|
|
|
@@ -21,8 +22,6 @@ const ui: CollageMakerUI = {
|
|
|
21
22
|
errorMin: "Нужно как минимум 2 изображения",
|
|
22
23
|
errorMax: "Максимум 9 изображений",
|
|
23
24
|
errorLoad: "Ошибка при загрузке изображений",
|
|
24
|
-
faqTitle: "Часто задаваемые вопросы",
|
|
25
|
-
bibliographyTitle: "Ссылки"
|
|
26
25
|
};
|
|
27
26
|
|
|
28
27
|
const faq: CollageMakerLocaleContent['faq'] = [
|
|
@@ -55,13 +54,6 @@ const howTo: CollageMakerLocaleContent['howTo'] = [
|
|
|
55
54
|
},
|
|
56
55
|
];
|
|
57
56
|
|
|
58
|
-
const bibliography: CollageMakerLocaleContent['bibliography'] = [
|
|
59
|
-
{
|
|
60
|
-
name: "Фотокомпозиция: искусство коллажа",
|
|
61
|
-
url: "https://ru.wikipedia.org/wiki/Коллаж",
|
|
62
|
-
},
|
|
63
|
-
];
|
|
64
|
-
|
|
65
57
|
const seo: CollageMakerLocaleContent['seo'] = [
|
|
66
58
|
{
|
|
67
59
|
type: 'summary',
|
|
@@ -224,9 +216,7 @@ export const content: CollageMakerLocaleContent = {
|
|
|
224
216
|
description,
|
|
225
217
|
ui,
|
|
226
218
|
seo,
|
|
227
|
-
faqTitle: "Часто задаваемые вопросы",
|
|
228
219
|
faq,
|
|
229
|
-
bibliographyTitle: "Ссылки",
|
|
230
220
|
bibliography,
|
|
231
221
|
howTo,
|
|
232
222
|
schemas: [faqSchema as any, howToSchema as any, appSchema],
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { bibliography } from '../bibliography';
|
|
1
2
|
import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
|
|
2
3
|
import type { CollageMakerUI, CollageMakerLocaleContent } from '../index';
|
|
3
4
|
|
|
@@ -21,8 +22,6 @@ const ui: CollageMakerUI = {
|
|
|
21
22
|
errorMin: "Du behöver minst 2 bilder",
|
|
22
23
|
errorMax: "Maximalt 9 bilder tillåtna",
|
|
23
24
|
errorLoad: "Fel vid laddning av bilder",
|
|
24
|
-
faqTitle: "Vanliga frågor",
|
|
25
|
-
bibliographyTitle: "Referenser"
|
|
26
25
|
};
|
|
27
26
|
|
|
28
27
|
const faq: CollageMakerLocaleContent['faq'] = [
|
|
@@ -55,13 +54,6 @@ const howTo: CollageMakerLocaleContent['howTo'] = [
|
|
|
55
54
|
},
|
|
56
55
|
];
|
|
57
56
|
|
|
58
|
-
const bibliography: CollageMakerLocaleContent['bibliography'] = [
|
|
59
|
-
{
|
|
60
|
-
name: "Fotografisk komposition: Konsten att skapa collage",
|
|
61
|
-
url: "https://sv.wikipedia.org/wiki/Collage",
|
|
62
|
-
},
|
|
63
|
-
];
|
|
64
|
-
|
|
65
57
|
const seo: CollageMakerLocaleContent['seo'] = [
|
|
66
58
|
{
|
|
67
59
|
type: 'summary',
|
|
@@ -224,9 +216,7 @@ export const content: CollageMakerLocaleContent = {
|
|
|
224
216
|
description,
|
|
225
217
|
ui,
|
|
226
218
|
seo,
|
|
227
|
-
faqTitle: "Vanliga frågor",
|
|
228
219
|
faq,
|
|
229
|
-
bibliographyTitle: "Referenser",
|
|
230
220
|
bibliography,
|
|
231
221
|
howTo,
|
|
232
222
|
schemas: [faqSchema as any, howToSchema as any, appSchema],
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { bibliography } from '../bibliography';
|
|
1
2
|
import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
|
|
2
3
|
import type { CollageMakerUI, CollageMakerLocaleContent } from '../index';
|
|
3
4
|
|
|
@@ -21,8 +22,6 @@ const ui: CollageMakerUI = {
|
|
|
21
22
|
errorMin: "En az 2 görsele ihtiyacınız var",
|
|
22
23
|
errorMax: "Maksimum 9 görsele izin verilir",
|
|
23
24
|
errorLoad: "Görseller yüklenirken hata oluştu",
|
|
24
|
-
faqTitle: "Sıkça Sorulan Sorular",
|
|
25
|
-
bibliographyTitle: "Referanslar"
|
|
26
25
|
};
|
|
27
26
|
|
|
28
27
|
const faq: CollageMakerLocaleContent['faq'] = [
|
|
@@ -55,13 +54,6 @@ const howTo: CollageMakerLocaleContent['howTo'] = [
|
|
|
55
54
|
},
|
|
56
55
|
];
|
|
57
56
|
|
|
58
|
-
const bibliography: CollageMakerLocaleContent['bibliography'] = [
|
|
59
|
-
{
|
|
60
|
-
name: "Fotoğrafik Kompozisyon: Kolaj Sanatı",
|
|
61
|
-
url: "https://tr.wikipedia.org/wiki/Kolaj",
|
|
62
|
-
},
|
|
63
|
-
];
|
|
64
|
-
|
|
65
57
|
const seo: CollageMakerLocaleContent['seo'] = [
|
|
66
58
|
{
|
|
67
59
|
type: 'summary',
|
|
@@ -224,9 +216,7 @@ export const content: CollageMakerLocaleContent = {
|
|
|
224
216
|
description,
|
|
225
217
|
ui,
|
|
226
218
|
seo,
|
|
227
|
-
faqTitle: "Sıkça Sorulan Sorular",
|
|
228
219
|
faq,
|
|
229
|
-
bibliographyTitle: "Referanslar",
|
|
230
220
|
bibliography,
|
|
231
221
|
howTo,
|
|
232
222
|
schemas: [faqSchema as any, howToSchema as any, appSchema],
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { bibliography } from '../bibliography';
|
|
1
2
|
import type { WithContext, FAQPage, HowTo, SoftwareApplication } from 'schema-dts';
|
|
2
3
|
import type { CollageMakerUI, CollageMakerLocaleContent } from '../index';
|
|
3
4
|
|
|
@@ -21,8 +22,6 @@ const ui: CollageMakerUI = {
|
|
|
21
22
|
errorMin: "您至少需要 2 张图像",
|
|
22
23
|
errorMax: "最多允许 9 张图像",
|
|
23
24
|
errorLoad: "加载图像时出错",
|
|
24
|
-
faqTitle: "常见问题",
|
|
25
|
-
bibliographyTitle: "参考资料"
|
|
26
25
|
};
|
|
27
26
|
|
|
28
27
|
const faq: CollageMakerLocaleContent['faq'] = [
|
|
@@ -55,13 +54,6 @@ const howTo: CollageMakerLocaleContent['howTo'] = [
|
|
|
55
54
|
},
|
|
56
55
|
];
|
|
57
56
|
|
|
58
|
-
const bibliography: CollageMakerLocaleContent['bibliography'] = [
|
|
59
|
-
{
|
|
60
|
-
name: "摄影构图:拼贴的艺术",
|
|
61
|
-
url: "https://zh.wikipedia.org/wiki/%E5%89%AA%E8%B4%B4%E7%94%BB",
|
|
62
|
-
},
|
|
63
|
-
];
|
|
64
|
-
|
|
65
57
|
const seo: CollageMakerLocaleContent['seo'] = [
|
|
66
58
|
{
|
|
67
59
|
type: 'summary',
|
|
@@ -224,9 +216,7 @@ export const content: CollageMakerLocaleContent = {
|
|
|
224
216
|
description,
|
|
225
217
|
ui,
|
|
226
218
|
seo,
|
|
227
|
-
faqTitle: "常见问题",
|
|
228
219
|
faq,
|
|
229
|
-
bibliographyTitle: "参考资料",
|
|
230
220
|
bibliography,
|
|
231
221
|
howTo,
|
|
232
222
|
schemas: [faqSchema as any, howToSchema as any, appSchema],
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { Bibliography as SharedBibliography } from '@jjlmoya/utils-shared';
|
|
3
|
+
import { DEPTH_OF_FIELD_CALCULATOR_TOOL } from './index';
|
|
4
|
+
import type { KnownLocale } from '../../types';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
locale?: KnownLocale;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { locale = 'en' } = Astro.props;
|
|
11
|
+
const content = await DEPTH_OF_FIELD_CALCULATOR_TOOL.entry.i18n[locale]?.();
|
|
12
|
+
const { bibliography } = content || {};
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
{bibliography && <SharedBibliography links={bibliography} />}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { BibliographyEntry } from '../../types';
|
|
2
|
+
|
|
3
|
+
export const bibliography: BibliographyEntry[] = [
|
|
4
|
+
{
|
|
5
|
+
name: "Nikon - Understanding Depth of Field",
|
|
6
|
+
url: "https://nikonschool.co.uk/hints-and-tips/understanding-depth-of-field",
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
name: "Cambridge in Colour - Depth of Field Tutorial",
|
|
10
|
+
url: "https://www.cambridgeincolour.com/tutorials/depth-of-field.htm",
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
name: "Zeiss - Circle of Confusion and Depth of Field",
|
|
14
|
+
url: "https://www.zeiss.com/consumer-products/int/photography/lenses.html",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: "PhotoPills - Depth of Field Calculator",
|
|
18
|
+
url: "https://www.photopills.com/calculators/dof",
|
|
19
|
+
},
|
|
20
|
+
];
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { Icon } from 'astro-icon/components';
|
|
3
|
+
import type { DepthOfFieldUI } from './index';
|
|
4
|
+
import { SENSOR_PRESETS, QUICK_SENSOR_KEYS } from './logic';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
ui: DepthOfFieldUI;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { ui } = Astro.props;
|
|
11
|
+
|
|
12
|
+
const quickSensors = SENSOR_PRESETS.filter(s => QUICK_SENSOR_KEYS.includes(s.key));
|
|
13
|
+
const moreSensors = SENSOR_PRESETS.filter(s => !QUICK_SENSOR_KEYS.includes(s.key));
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
<div class="dof-root">
|
|
17
|
+
<div class="dof-grid">
|
|
18
|
+
|
|
19
|
+
<div class="dof-inputs-panel">
|
|
20
|
+
<h3 class="dof-panel-title">
|
|
21
|
+
<Icon name="mdi:tune-vertical" class="dof-panel-icon" />
|
|
22
|
+
{ui.paramsTitle}
|
|
23
|
+
</h3>
|
|
24
|
+
|
|
25
|
+
<div class="dof-field-group">
|
|
26
|
+
<label class="dof-group-label">{ui.sensorLabel}</label>
|
|
27
|
+
<div class="dof-sensor-buttons">
|
|
28
|
+
{quickSensors.map(s => (
|
|
29
|
+
<button
|
|
30
|
+
class={`dof-sensor-btn${s.key === 'ff' ? ' dof-sensor-btn-active' : ''}`}
|
|
31
|
+
data-sensor={s.key}
|
|
32
|
+
data-coc={s.coc}
|
|
33
|
+
>
|
|
34
|
+
{s.shortLabel}
|
|
35
|
+
</button>
|
|
36
|
+
))}
|
|
37
|
+
</div>
|
|
38
|
+
<div class="dof-sensor-row">
|
|
39
|
+
<div class="dof-select-wrapper">
|
|
40
|
+
<select id="dof-sensor-more" class="dof-select">
|
|
41
|
+
<option value="">{ui.moreLabel}</option>
|
|
42
|
+
{moreSensors.map(s => (
|
|
43
|
+
<option value={s.key} data-coc={s.coc}>{s.label}</option>
|
|
44
|
+
))}
|
|
45
|
+
</select>
|
|
46
|
+
<Icon name="mdi:chevron-down" class="dof-select-arrow" />
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
|
|
51
|
+
<div class="dof-field-group">
|
|
52
|
+
<div class="dof-slider-header">
|
|
53
|
+
<label class="dof-group-label" for="dof-aperture-slider">{ui.apertureLabel}</label>
|
|
54
|
+
<span id="dof-aperture-display" class="dof-slider-value">f/2.8</span>
|
|
55
|
+
</div>
|
|
56
|
+
<input
|
|
57
|
+
type="range"
|
|
58
|
+
id="dof-aperture-slider"
|
|
59
|
+
class="dof-slider"
|
|
60
|
+
min="0"
|
|
61
|
+
max="27"
|
|
62
|
+
value="9"
|
|
63
|
+
step="1"
|
|
64
|
+
|
|
65
|
+
style="--pct: 33%"
|
|
66
|
+
/>
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<div class="dof-field-group">
|
|
70
|
+
<div class="dof-slider-header">
|
|
71
|
+
<label class="dof-group-label" for="dof-focal-slider">{ui.focalLabel}</label>
|
|
72
|
+
<span id="dof-focal-display" class="dof-slider-value">50 mm</span>
|
|
73
|
+
</div>
|
|
74
|
+
<input
|
|
75
|
+
type="range"
|
|
76
|
+
id="dof-focal-slider"
|
|
77
|
+
class="dof-slider"
|
|
78
|
+
min="0"
|
|
79
|
+
max="99"
|
|
80
|
+
value="39"
|
|
81
|
+
step="1"
|
|
82
|
+
|
|
83
|
+
style="--pct: 39%"
|
|
84
|
+
/>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<div class="dof-field-group">
|
|
88
|
+
<div class="dof-distance-header">
|
|
89
|
+
<label class="dof-group-label" for="dof-subject-slider">{ui.distanceLabel}</label>
|
|
90
|
+
<div class="dof-unit-toggle" role="group">
|
|
91
|
+
<button id="dof-unit-m" class="dof-unit-btn dof-unit-btn-active">{ui.metersLabel}</button>
|
|
92
|
+
<button id="dof-unit-ft" class="dof-unit-btn">{ui.feetLabel}</button>
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
<div class="dof-slider-header" style="margin-top: 0.25rem;">
|
|
96
|
+
<span></span>
|
|
97
|
+
<span id="dof-subject-display" class="dof-slider-value">3.00 m</span>
|
|
98
|
+
</div>
|
|
99
|
+
<input
|
|
100
|
+
type="range"
|
|
101
|
+
id="dof-subject-slider"
|
|
102
|
+
class="dof-slider"
|
|
103
|
+
min="0"
|
|
104
|
+
max="99"
|
|
105
|
+
value="30"
|
|
106
|
+
step="1"
|
|
107
|
+
|
|
108
|
+
style="--pct: 30%"
|
|
109
|
+
/>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<div class="dof-results-panel">
|
|
114
|
+
<h3 class="dof-results-title">
|
|
115
|
+
<Icon name="mdi:image-filter-center-focus" class="dof-results-icon" />
|
|
116
|
+
{ui.resultsTitle}
|
|
117
|
+
</h3>
|
|
118
|
+
|
|
119
|
+
<div class="dof-primary">
|
|
120
|
+
<p class="dof-primary-label">{ui.totalDofLabel}</p>
|
|
121
|
+
<p id="dof-total" class="dof-primary-value">—</p>
|
|
122
|
+
</div>
|
|
123
|
+
|
|
124
|
+
<div id="dof-error" class="dof-error">{ui.errorBelowFocal}</div>
|
|
125
|
+
|
|
126
|
+
<div class="dof-bar-wrapper">
|
|
127
|
+
<svg id="dof-bar-svg" class="dof-bar-svg" viewBox="0 0 1000 80">
|
|
128
|
+
<defs>
|
|
129
|
+
<linearGradient id="dof-inf-grad" x1="0" y1="0" x2="1" y2="0" gradientUnits="objectBoundingBox">
|
|
130
|
+
<stop offset="0%" stop-color="#818cf8" stop-opacity="0.85"/>
|
|
131
|
+
<stop offset="100%" stop-color="#818cf8" stop-opacity="0"/>
|
|
132
|
+
</linearGradient>
|
|
133
|
+
</defs>
|
|
134
|
+
<rect x="0" y="28" width="1000" height="24" rx="12" fill="rgba(255,255,255,0.1)"/>
|
|
135
|
+
<rect id="dof-svg-zone" x="300" y="22" width="400" height="36" rx="8" fill="rgba(165,180,252,0.65)"/>
|
|
136
|
+
<rect id="dof-svg-infinite" x="300" y="22" width="700" height="36" rx="8" fill="url(#dof-inf-grad)" display="none"/>
|
|
137
|
+
<line x1="500" y1="8" x2="500" y2="72" stroke="rgba(255,255,255,0.7)" stroke-width="2" stroke-dasharray="5,3"/>
|
|
138
|
+
<circle cx="500" cy="40" r="7" fill="white" stroke="rgba(165,180,252,0.6)" stroke-width="2"/>
|
|
139
|
+
<text id="dof-svg-near-txt" x="300" y="76" text-anchor="middle" fill="rgba(255,255,255,0.55)" font-size="10" font-family="system-ui,sans-serif">Near</text>
|
|
140
|
+
<text id="dof-svg-far-txt" x="700" y="76" text-anchor="middle" fill="rgba(255,255,255,0.55)" font-size="10" font-family="system-ui,sans-serif">Far</text>
|
|
141
|
+
<text x="500" y="7" text-anchor="middle" fill="rgba(255,255,255,0.4)" font-size="8" font-family="system-ui,sans-serif">Subject</text>
|
|
142
|
+
</svg>
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
<div class="dof-stats-grid">
|
|
146
|
+
<div class="dof-stat">
|
|
147
|
+
<p class="dof-stat-label">{ui.nearLimitLabel}</p>
|
|
148
|
+
<p id="dof-near" class="dof-stat-value">—</p>
|
|
149
|
+
</div>
|
|
150
|
+
<div class="dof-stat">
|
|
151
|
+
<p class="dof-stat-label">{ui.farLimitLabel}</p>
|
|
152
|
+
<p id="dof-far" class="dof-stat-value">—</p>
|
|
153
|
+
</div>
|
|
154
|
+
<div class="dof-stat">
|
|
155
|
+
<p class="dof-stat-label">{ui.hyperfocalLabel}</p>
|
|
156
|
+
<p id="dof-hyperfocal" class="dof-stat-value">—</p>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
|
|
160
|
+
<div class="dof-details">
|
|
161
|
+
<button id="dof-details-toggle" class="dof-details-toggle">
|
|
162
|
+
<Icon name="mdi:chevron-down" id="dof-details-icon" class="dof-details-icon" />
|
|
163
|
+
{ui.showDetailsLabel}
|
|
164
|
+
</button>
|
|
165
|
+
<div id="dof-details-content" class="dof-details-content">
|
|
166
|
+
{ui.cocLabel}: <span id="dof-coc-value" class="dof-coc-value">0.030</span> {ui.cocUnit}
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
</div>
|
|
170
|
+
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
|
|
174
|
+
<script>
|
|
175
|
+
import {
|
|
176
|
+
calculateDoF, formatDistance,
|
|
177
|
+
SENSOR_PRESETS, QUICK_SENSOR_KEYS,
|
|
178
|
+
F_STOPS,
|
|
179
|
+
sliderToFocal, sliderToSubject,
|
|
180
|
+
} from './logic';
|
|
181
|
+
|
|
182
|
+
const SVG_W = 1000;
|
|
183
|
+
const SLIDER_STEPS = 100;
|
|
184
|
+
const LS_SENSOR = 'dof-sensor';
|
|
185
|
+
const LS_UNIT = 'dof-unit';
|
|
186
|
+
|
|
187
|
+
let activeSensorKey = localStorage.getItem(LS_SENSOR) ?? 'ff';
|
|
188
|
+
let useFeet = localStorage.getItem(LS_UNIT) === 'ft';
|
|
189
|
+
|
|
190
|
+
function getSensorCoc(key: string): number {
|
|
191
|
+
return SENSOR_PRESETS.find(s => s.key === key)?.coc ?? 0.030;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const sensorBtns = document.querySelectorAll<HTMLButtonElement>('.dof-sensor-btn');
|
|
195
|
+
const sensorMore = document.getElementById('dof-sensor-more') as HTMLSelectElement;
|
|
196
|
+
const apertureSlider = document.getElementById('dof-aperture-slider') as HTMLInputElement;
|
|
197
|
+
const apertureDisplay = document.getElementById('dof-aperture-display')!;
|
|
198
|
+
const focalSlider = document.getElementById('dof-focal-slider') as HTMLInputElement;
|
|
199
|
+
const focalDisplay = document.getElementById('dof-focal-display')!;
|
|
200
|
+
const subjectSlider = document.getElementById('dof-subject-slider') as HTMLInputElement;
|
|
201
|
+
const subjectDisplay = document.getElementById('dof-subject-display')!;
|
|
202
|
+
const unitM = document.getElementById('dof-unit-m') as HTMLButtonElement;
|
|
203
|
+
const unitFt = document.getElementById('dof-unit-ft') as HTMLButtonElement;
|
|
204
|
+
const totalEl = document.getElementById('dof-total')!;
|
|
205
|
+
const nearEl = document.getElementById('dof-near')!;
|
|
206
|
+
const farEl = document.getElementById('dof-far')!;
|
|
207
|
+
const hyperfocalEl = document.getElementById('dof-hyperfocal')!;
|
|
208
|
+
const errorEl = document.getElementById('dof-error')!;
|
|
209
|
+
const svgZone = document.getElementById('dof-svg-zone')!;
|
|
210
|
+
const svgInfinite = document.getElementById('dof-svg-infinite')!;
|
|
211
|
+
const svgNearTxt = document.getElementById('dof-svg-near-txt')!;
|
|
212
|
+
const svgFarTxt = document.getElementById('dof-svg-far-txt')!;
|
|
213
|
+
const detailsToggle = document.getElementById('dof-details-toggle')!;
|
|
214
|
+
const detailsContent = document.getElementById('dof-details-content')!;
|
|
215
|
+
const detailsIcon = document.getElementById('dof-details-icon')!;
|
|
216
|
+
const cocValueEl = document.getElementById('dof-coc-value')!;
|
|
217
|
+
|
|
218
|
+
function setSliderFill(el: HTMLInputElement) {
|
|
219
|
+
const pct = ((+el.value - +el.min) / (+el.max - +el.min)) * 100;
|
|
220
|
+
el.style.setProperty('--pct', `${pct}%`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function activateSensor(key: string) {
|
|
224
|
+
activeSensorKey = key;
|
|
225
|
+
localStorage.setItem(LS_SENSOR, key);
|
|
226
|
+
|
|
227
|
+
sensorBtns.forEach(btn => {
|
|
228
|
+
btn.classList.toggle('dof-sensor-btn-active', btn.dataset.sensor === key);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
const isQuick = QUICK_SENSOR_KEYS.includes(key);
|
|
232
|
+
if (!isQuick) {
|
|
233
|
+
sensorMore.value = key;
|
|
234
|
+
} else {
|
|
235
|
+
sensorMore.value = '';
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const coc = getSensorCoc(key);
|
|
239
|
+
cocValueEl.textContent = coc.toFixed(3);
|
|
240
|
+
recalculate();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function setUnit(feet: boolean) {
|
|
244
|
+
useFeet = feet;
|
|
245
|
+
localStorage.setItem(LS_UNIT, feet ? 'ft' : 'm');
|
|
246
|
+
unitM.classList.toggle('dof-unit-btn-active', !feet);
|
|
247
|
+
unitFt.classList.toggle('dof-unit-btn-active', feet);
|
|
248
|
+
recalculate();
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function updateFocusBar(subject: number, nearLimit: number, farLimit: number, isInfinite: boolean) {
|
|
252
|
+
const frontDoF = subject - nearLimit;
|
|
253
|
+
const backDoF = isInfinite ? subject * 2 : farLimit - subject;
|
|
254
|
+
const halfRange = Math.max(frontDoF * 4, backDoF * 2, subject * 0.3, 0.5);
|
|
255
|
+
|
|
256
|
+
const nearX = Math.max(0, (0.5 - frontDoF / (2 * halfRange)) * SVG_W);
|
|
257
|
+
const farX = isInfinite ? SVG_W : Math.min(SVG_W, (0.5 + backDoF / (2 * halfRange)) * SVG_W);
|
|
258
|
+
|
|
259
|
+
if (isInfinite) {
|
|
260
|
+
svgZone.setAttribute('display', 'none');
|
|
261
|
+
svgInfinite.setAttribute('display', 'block');
|
|
262
|
+
svgInfinite.setAttribute('x', String(nearX));
|
|
263
|
+
svgInfinite.setAttribute('width', String(SVG_W - nearX));
|
|
264
|
+
svgFarTxt.textContent = '∞';
|
|
265
|
+
svgFarTxt.setAttribute('x', String(SVG_W - 20));
|
|
266
|
+
} else {
|
|
267
|
+
svgZone.setAttribute('display', 'block');
|
|
268
|
+
svgInfinite.setAttribute('display', 'none');
|
|
269
|
+
svgZone.setAttribute('x', String(nearX));
|
|
270
|
+
svgZone.setAttribute('width', String(farX - nearX));
|
|
271
|
+
const farLabel = formatDistance(farLimit, useFeet);
|
|
272
|
+
svgFarTxt.textContent = farLabel;
|
|
273
|
+
svgFarTxt.setAttribute('x', String(Math.min(farX, SVG_W - farLabel.length * 4)));
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const nearLabel = formatDistance(nearLimit, useFeet);
|
|
277
|
+
svgNearTxt.textContent = nearLabel;
|
|
278
|
+
svgNearTxt.setAttribute('x', String(Math.max(nearX, nearLabel.length * 4)));
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function recalculate() {
|
|
282
|
+
const apertureIdx = +apertureSlider.value;
|
|
283
|
+
const aperture = F_STOPS[apertureIdx] ?? 2.8;
|
|
284
|
+
const focalMm = sliderToFocal(+focalSlider.value, SLIDER_STEPS);
|
|
285
|
+
const subjectM = sliderToSubject(+subjectSlider.value, SLIDER_STEPS);
|
|
286
|
+
const coc = getSensorCoc(activeSensorKey);
|
|
287
|
+
|
|
288
|
+
apertureDisplay.textContent = `f/${aperture}`;
|
|
289
|
+
focalDisplay.textContent = `${focalMm} mm`;
|
|
290
|
+
subjectDisplay.textContent = formatDistance(subjectM, useFeet);
|
|
291
|
+
|
|
292
|
+
setSliderFill(apertureSlider);
|
|
293
|
+
setSliderFill(focalSlider);
|
|
294
|
+
setSliderFill(subjectSlider);
|
|
295
|
+
|
|
296
|
+
const r = calculateDoF(subjectM, focalMm, aperture, coc);
|
|
297
|
+
|
|
298
|
+
if (r.hasError) {
|
|
299
|
+
errorEl.style.display = 'block';
|
|
300
|
+
totalEl.textContent = '—';
|
|
301
|
+
nearEl.textContent = '—';
|
|
302
|
+
farEl.textContent = '—';
|
|
303
|
+
hyperfocalEl.textContent = '—';
|
|
304
|
+
svgZone.setAttribute('display', 'none');
|
|
305
|
+
svgInfinite.setAttribute('display', 'none');
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
errorEl.style.display = 'none';
|
|
310
|
+
totalEl.textContent = r.isInfinite ? '∞' : formatDistance(r.totalDoF, useFeet);
|
|
311
|
+
nearEl.textContent = formatDistance(r.nearLimit, useFeet);
|
|
312
|
+
farEl.textContent = r.isInfinite ? '∞' : formatDistance(r.farLimit, useFeet);
|
|
313
|
+
hyperfocalEl.textContent = formatDistance(r.hyperfocal, useFeet);
|
|
314
|
+
|
|
315
|
+
updateFocusBar(subjectM, r.nearLimit, r.farLimit, r.isInfinite);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
detailsToggle.addEventListener('click', () => {
|
|
319
|
+
const isOpen = detailsContent.classList.toggle('dof-details-content-visible');
|
|
320
|
+
detailsIcon.classList.toggle('dof-details-icon-open', isOpen);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
sensorBtns.forEach(btn => {
|
|
324
|
+
btn.addEventListener('click', () => activateSensor(btn.dataset.sensor!));
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
sensorMore.addEventListener('change', () => {
|
|
328
|
+
if (sensorMore.value) activateSensor(sensorMore.value);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
unitM.addEventListener('click', () => setUnit(false));
|
|
332
|
+
unitFt.addEventListener('click', () => setUnit(true));
|
|
333
|
+
|
|
334
|
+
[apertureSlider, focalSlider, subjectSlider].forEach(el => {
|
|
335
|
+
el.addEventListener('input', recalculate);
|
|
336
|
+
el.addEventListener('change', recalculate);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
if (useFeet) setUnit(true);
|
|
340
|
+
activateSensor(activeSensorKey);
|
|
341
|
+
</script>
|