@jjlmoya/utils-audiovisual 1.17.0 → 1.18.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 +2 -0
- package/src/entries.ts +4 -1
- package/src/index.ts +1 -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/tools.ts +2 -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>
|