@xwadex/fesd 0.0.1
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/20240328-video4-setting.png +0 -0
- package/CHANGELOG.md +41 -0
- package/README.md +25 -0
- package/dist/assets/fesd-bundle.css +9 -0
- package/dist/assets/fesd-bundle.js +9800 -0
- package/dist/assets/fesd-bundle.js.map +1 -0
- package/index.html +25 -0
- package/package.json +23 -0
- package/prepros.config +883 -0
- package/src/fesd/anchor4/anchor4.js +179 -0
- package/src/fesd/aost4/_aost4.sass +64 -0
- package/src/fesd/aost4/aost4.js +138 -0
- package/src/fesd/article4/article4.js +280 -0
- package/src/fesd/article4/article4.md +1 -0
- package/src/fesd/category-slider/_category-slider.sass +33 -0
- package/src/fesd/category-slider/category-slider.js +332 -0
- package/src/fesd/collapse4/collapse4.js +159 -0
- package/src/fesd/detect4/detect4.js +70 -0
- package/src/fesd/dropdown4/_dropdown4.sass +185 -0
- package/src/fesd/dropdown4/cityData.js +830 -0
- package/src/fesd/dropdown4/dropdown4.js +647 -0
- package/src/fesd/image-preview/_image-preview.sass +26 -0
- package/src/fesd/image-preview/image-preview.js +209 -0
- package/src/fesd/image-validate/_image-validate.sass +21 -0
- package/src/fesd/image-validate/image-validate.js +84 -0
- package/src/fesd/marquee4/_marquee4.sass +45 -0
- package/src/fesd/marquee4/marquee4.js +371 -0
- package/src/fesd/modal4/_modal4.sass +134 -0
- package/src/fesd/modal4/modal4.js +236 -0
- package/src/fesd/modal4/modernModal.js +182 -0
- package/src/fesd/multipurpose4/_multipurpose4.sass +282 -0
- package/src/fesd/multipurpose4/multipurpose4.js +562 -0
- package/src/fesd/ripple4/_ripple4.sass +44 -0
- package/src/fesd/ripple4/ripple4.js +138 -0
- package/src/fesd/share4/share4.js +191 -0
- package/src/fesd/shared/shared.js +59 -0
- package/src/fesd/shared/utils.js +98 -0
- package/src/fesd/tab4/_tab4.sass +25 -0
- package/src/fesd/tab4/tab4.js +473 -0
- package/src/fesd/video4/README.md +3 -0
- package/src/fesd/video4/_video4.sass +117 -0
- package/src/fesd/video4/video4.js +237 -0
- package/src/fesd/video4/videoPlayer.js +195 -0
- package/src/fesd.js +53 -0
- package/src/fesd.sass +29 -0
- package/src/fesdDB.js +282 -0
- package/vite.config.js +37 -0
@@ -0,0 +1,209 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
//建立 callback Function
|
4
|
+
function createCallbackFunction(attributeValue, defaultValue) {
|
5
|
+
if (attributeValue) {
|
6
|
+
return function (el, res) {
|
7
|
+
Function(attributeValue)(el, res);
|
8
|
+
};
|
9
|
+
} else {
|
10
|
+
return defaultValue;
|
11
|
+
}
|
12
|
+
}
|
13
|
+
|
14
|
+
//產生結構
|
15
|
+
function createDOM(input, element, file, res) {
|
16
|
+
const params = element.params;
|
17
|
+
const preview = input.closest('[data-upload-item]').querySelector('[data-preview]');
|
18
|
+
if (preview.querySelector('img')) preview.querySelector('img').remove();
|
19
|
+
preview.appendChild(res.imgElement);
|
20
|
+
const group = input.dataset.group;
|
21
|
+
const inputIndex = document.querySelector(element.el).dataset.index;
|
22
|
+
if (params.compress) {
|
23
|
+
//副檔名
|
24
|
+
const fileExt = file.name.substring(file.name.lastIndexOf('.')).replace('.');
|
25
|
+
//檔名
|
26
|
+
const fileName = file.name.replace(`.${fileExt}`, '');
|
27
|
+
//檔案壓縮
|
28
|
+
res.originalCanvas.toBlob(
|
29
|
+
function (blob) {
|
30
|
+
const imgFile = new File([blob], `${fileName}`, { type: file.type });
|
31
|
+
uploadImage[`${group}`][inputIndex] = {
|
32
|
+
file: imgFile,
|
33
|
+
info: res,
|
34
|
+
};
|
35
|
+
if (params.on.changeAfter && typeof params.on.changeAfter === 'function') {
|
36
|
+
params.on.changeAfter(input, res);
|
37
|
+
}
|
38
|
+
},
|
39
|
+
file.type,
|
40
|
+
params.compress,
|
41
|
+
);
|
42
|
+
} else {
|
43
|
+
uploadImage[`${group}`][inputIndex] = {
|
44
|
+
file: file,
|
45
|
+
info: res,
|
46
|
+
};
|
47
|
+
if (params.on.changeAfter && typeof params.on.changeAfter === 'function') {
|
48
|
+
params.on.changeAfter(input, res);
|
49
|
+
}
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
//產生Canvas
|
54
|
+
function createCanvas(options, img, outputW, outputH) {
|
55
|
+
const canvas = document.createElement('canvas');
|
56
|
+
const ctx = canvas.getContext('2d');
|
57
|
+
const originalWidth = img.width;
|
58
|
+
const originalHeight = img.height;
|
59
|
+
const originalRatio = (originalHeight / originalWidth) * 100;
|
60
|
+
const outputRatio = (outputH / outputW) * 100;
|
61
|
+
let newWidth = 0;
|
62
|
+
let newHeight = 0;
|
63
|
+
canvas.width = outputW;
|
64
|
+
canvas.height = outputH;
|
65
|
+
//縮圖填滿
|
66
|
+
switch (options.previewSize) {
|
67
|
+
case 'contain':
|
68
|
+
if (originalRatio < outputRatio) {
|
69
|
+
newWidth = canvas.width;
|
70
|
+
newHeight = (outputW * originalRatio) / 100;
|
71
|
+
} else if (originalRatio > outputRatio) {
|
72
|
+
newWidth = (outputH / originalRatio) * 100;
|
73
|
+
newHeight = canvas.height;
|
74
|
+
} else {
|
75
|
+
newWidth = canvas.width;
|
76
|
+
newHeight = canvas.height;
|
77
|
+
}
|
78
|
+
break;
|
79
|
+
case 'cover':
|
80
|
+
if (originalRatio < outputRatio) {
|
81
|
+
newWidth = (outputH / originalRatio) * 100;
|
82
|
+
newHeight = canvas.height;
|
83
|
+
} else if (originalRatio > outputRatio) {
|
84
|
+
newWidth = canvas.width;
|
85
|
+
newHeight = (outputW * originalRatio) / 100;
|
86
|
+
} else if (originalRatio === outputRatio) {
|
87
|
+
newWidth = canvas.width;
|
88
|
+
newHeight = canvas.height;
|
89
|
+
}
|
90
|
+
break;
|
91
|
+
}
|
92
|
+
const x = (canvas.width - newWidth) * 0.5;
|
93
|
+
const y = (canvas.height - newHeight) * 0.5;
|
94
|
+
ctx.drawImage(img, x, y, newWidth, newHeight);
|
95
|
+
|
96
|
+
return canvas;
|
97
|
+
}
|
98
|
+
|
99
|
+
//將檔案轉成圖片
|
100
|
+
function convertImg(options, dataUrl) {
|
101
|
+
return new Promise((resolve, reject) => {
|
102
|
+
if (dataUrl) {
|
103
|
+
const img = new Image();
|
104
|
+
img.src = dataUrl;
|
105
|
+
img.classList.add(options.previewSize);
|
106
|
+
img.onload = () => {
|
107
|
+
let result = {
|
108
|
+
imgElement: img,
|
109
|
+
originalCanvas: createCanvas(options, img, img.width, img.height),
|
110
|
+
info: {
|
111
|
+
originalWidth: img.width,
|
112
|
+
originalHeight: img.height,
|
113
|
+
ratio: (img.height / img.width) * 100 + '%',
|
114
|
+
},
|
115
|
+
};
|
116
|
+
resolve(result);
|
117
|
+
};
|
118
|
+
} else {
|
119
|
+
resolve();
|
120
|
+
}
|
121
|
+
});
|
122
|
+
}
|
123
|
+
|
124
|
+
//小數點後第N位四捨五入
|
125
|
+
function formatFloat(num, pos) {
|
126
|
+
const size = Math.pow(10, pos);
|
127
|
+
return Math.round(num * size) / size;
|
128
|
+
}
|
129
|
+
|
130
|
+
//檢查檔案大小
|
131
|
+
function checkSize(file, sizeLimit) {
|
132
|
+
let fileSize = file.size / 1024 / 1024;
|
133
|
+
return `${formatFloat(fileSize, 2)}` <= sizeLimit;
|
134
|
+
}
|
135
|
+
|
136
|
+
//讀取檔案
|
137
|
+
function readFileHandler(e, input, element) {
|
138
|
+
if (e.target.files.length <= 0) return;
|
139
|
+
const file = e.target.files[0];
|
140
|
+
const parentsEl = input.closest('[data-upload-item]');
|
141
|
+
let reader = new FileReader();
|
142
|
+
reader.onload = event => {
|
143
|
+
const dataUrl = file.type.split('/')[0] === 'image' ? event.target.result : null;
|
144
|
+
if (element.params.sizeLimit && !checkSize(file, element.params.sizeLimit)) {
|
145
|
+
input.value = '';
|
146
|
+
parentsEl.classList.add('over-limit');
|
147
|
+
if (element.params.on.overLimit && typeof element.params.on.overLimit === 'function') {
|
148
|
+
element.params.on.overLimit(input, element.params.sizeLimit);
|
149
|
+
}
|
150
|
+
return;
|
151
|
+
} else {
|
152
|
+
parentsEl.classList.remove('over-limit');
|
153
|
+
parentsEl.classList.add('uploaded');
|
154
|
+
convertImg(element.params, dataUrl).then(res => {
|
155
|
+
createDOM(input, element, file, res);
|
156
|
+
});
|
157
|
+
}
|
158
|
+
};
|
159
|
+
reader.readAsDataURL(file);
|
160
|
+
}
|
161
|
+
|
162
|
+
class ImagePreview {
|
163
|
+
constructor(element, params) {
|
164
|
+
const self = this;
|
165
|
+
self.el = element;
|
166
|
+
// default params
|
167
|
+
self.params = {
|
168
|
+
on: {
|
169
|
+
changeAfter: null,
|
170
|
+
overLimit: null,
|
171
|
+
},
|
172
|
+
group: 'group', //群組名稱 (type: String)
|
173
|
+
sizeLimit: 2, //大小限制 (type: Number 單位: MB)
|
174
|
+
previewSize: 'cover', //縮圖形式 (type: String 'contain' || 'cover')
|
175
|
+
compress: 0.8, //壓縮品質 (type: Number 0~1 || false)
|
176
|
+
};
|
177
|
+
Object.assign(self.params, params);
|
178
|
+
self.init();
|
179
|
+
}
|
180
|
+
init() {
|
181
|
+
const self = this;
|
182
|
+
const inputs = document.querySelectorAll(`${self.el}:not(.preview-active)`);
|
183
|
+
if (inputs.length <= 0) return;
|
184
|
+
window.uploadImage = {};
|
185
|
+
inputs.forEach((input, index) => {
|
186
|
+
input.classList.add('preview-active');
|
187
|
+
// final set params
|
188
|
+
const params = {
|
189
|
+
group: input.dataset.group || self.params.group,
|
190
|
+
sizeLimit: parseInt(input.dataset.limit) || self.params.sizeLimit,
|
191
|
+
previewSize: input.dataset.previewSize || self.params.previewSize,
|
192
|
+
compress: input.dataset.compress || self.params.compress,
|
193
|
+
on: {
|
194
|
+
changeAfter: createCallbackFunction(input.dataset.changeAfter, self.params.on.changeAfter),
|
195
|
+
overLimit: createCallbackFunction(input.dataset.overLimit, self.params.on.overLimit),
|
196
|
+
},
|
197
|
+
};
|
198
|
+
self.params = params;
|
199
|
+
if (typeof uploadImage[`${params.group}`] === 'undefined') uploadImage[`${params.group}`] = [];
|
200
|
+
if (!input.dataset.group) input.dataset.group = params.group;
|
201
|
+
input.dataset.index = document.querySelectorAll(`[data-group="${params.group}"]`).length - 1;
|
202
|
+
input.addEventListener('change', function (e) {
|
203
|
+
readFileHandler(e, input, self);
|
204
|
+
});
|
205
|
+
});
|
206
|
+
}
|
207
|
+
}
|
208
|
+
|
209
|
+
export default ImagePreview;
|
@@ -0,0 +1,21 @@
|
|
1
|
+
.no-image
|
2
|
+
position: absolute
|
3
|
+
top: 50%
|
4
|
+
left: 50%
|
5
|
+
transform: translate3d(-50%,-50%,0)
|
6
|
+
width: 100%
|
7
|
+
height: 100%
|
8
|
+
background: #F7F7F7
|
9
|
+
border: 1px solid #ABABAB
|
10
|
+
overflow: hidden
|
11
|
+
.slash1,.slash2
|
12
|
+
position: absolute
|
13
|
+
top: 0
|
14
|
+
height: 1px
|
15
|
+
background-color: #ABABAB
|
16
|
+
.slash1
|
17
|
+
left: 0
|
18
|
+
transform-origin: left top
|
19
|
+
.slash2
|
20
|
+
right: 0
|
21
|
+
transform-origin: right top
|
@@ -0,0 +1,84 @@
|
|
1
|
+
export default class ImageValidate {
|
2
|
+
constructor() {
|
3
|
+
this.init();
|
4
|
+
}
|
5
|
+
init() {
|
6
|
+
const images = document.querySelectorAll('*:not([video-id]) > img');
|
7
|
+
const noImageDom = (width, height) => {
|
8
|
+
const div = document.createElement('div');
|
9
|
+
const slash1 = document.createElement('span');
|
10
|
+
const slash2 = document.createElement('span');
|
11
|
+
const slashWidth = Math.sqrt(width ** 2 + height ** 2);
|
12
|
+
const theta = (Math.asin(height / slashWidth) * 180) / Math.PI;
|
13
|
+
div.className = 'no-image';
|
14
|
+
slash1.className = 'slash1';
|
15
|
+
slash1.style.cssText = `
|
16
|
+
width: ${slashWidth}px;
|
17
|
+
transform: rotate(${theta}deg);
|
18
|
+
`;
|
19
|
+
slash2.className = 'slash2';
|
20
|
+
slash2.style.cssText = `
|
21
|
+
width: ${slashWidth}px;
|
22
|
+
transform: rotate(${-theta}deg);
|
23
|
+
`;
|
24
|
+
div.appendChild(slash1);
|
25
|
+
div.appendChild(slash2);
|
26
|
+
return div;
|
27
|
+
};
|
28
|
+
const errorImage = document.querySelectorAll('.error-image');
|
29
|
+
for (let i = 0; i < errorImage.length; i++) {
|
30
|
+
errorImage[i].remove();
|
31
|
+
}
|
32
|
+
images.forEach((img) => {
|
33
|
+
let src;
|
34
|
+
const wrapWidth = img.parentElement.clientWidth;
|
35
|
+
const wrapHeight = img.parentElement.clientHeight;
|
36
|
+
if (img.classList.contains('lazy') || img.classList.contains('swiper-lazy')) {
|
37
|
+
src = img.getAttribute('data-src');
|
38
|
+
} else {
|
39
|
+
src = img.getAttribute('src');
|
40
|
+
}
|
41
|
+
if (src === '') {
|
42
|
+
const wrapDIV = document.createElement('div');
|
43
|
+
const parentsPosition = getComputedStyle(img.parentElement).position;
|
44
|
+
wrapDIV.className = 'error-image';
|
45
|
+
if (parentsPosition === 'static') {
|
46
|
+
img.parentElement.style.position = 'relative';
|
47
|
+
}
|
48
|
+
img.parentElement.appendChild(wrapDIV);
|
49
|
+
wrapDIV.style.cssText = `
|
50
|
+
position: absolute;
|
51
|
+
top: 50%;
|
52
|
+
left: 50%;
|
53
|
+
width: 100%;
|
54
|
+
height: 100%;
|
55
|
+
transform: translate(-50%,-50%);
|
56
|
+
z-index: 1;
|
57
|
+
`;
|
58
|
+
wrapDIV.appendChild(noImageDom(wrapWidth, wrapHeight));
|
59
|
+
}
|
60
|
+
});
|
61
|
+
function imageResize() {
|
62
|
+
const noImage = document.querySelectorAll('.no-image');
|
63
|
+
if (noImage.length <= 0) return;
|
64
|
+
noImage.forEach((item) => {
|
65
|
+
const wrapWidth = item.parentElement.clientWidth;
|
66
|
+
const wrapHeight = item.parentElement.clientHeight;
|
67
|
+
const slashWidth = Math.sqrt(wrapWidth ** 2 + wrapHeight ** 2);
|
68
|
+
const theta = (Math.asin(wrapHeight / slashWidth) * 180) / Math.PI;
|
69
|
+
item.querySelector('.slash1').style.cssText = `
|
70
|
+
width: ${slashWidth}px;
|
71
|
+
transform: rotate(${theta}deg);
|
72
|
+
`;
|
73
|
+
item.querySelector('.slash2').style.cssText = `
|
74
|
+
width: ${slashWidth}px;
|
75
|
+
transform: rotate(${-theta}deg);
|
76
|
+
`;
|
77
|
+
});
|
78
|
+
}
|
79
|
+
window.addEventListener('resize', imageResize);
|
80
|
+
}
|
81
|
+
reValidate() {
|
82
|
+
this.init();
|
83
|
+
}
|
84
|
+
}
|
@@ -0,0 +1,45 @@
|
|
1
|
+
marquee-el
|
2
|
+
display: flex
|
3
|
+
width: 100%
|
4
|
+
height: 100%
|
5
|
+
padding: 0 !important
|
6
|
+
border: none !important
|
7
|
+
overflow: hidden
|
8
|
+
&.continual
|
9
|
+
.animate-container
|
10
|
+
display: flex
|
11
|
+
width: 100%
|
12
|
+
overflow: hidden
|
13
|
+
&[direction="left"],&[direction="right"]
|
14
|
+
white-space: nowrap
|
15
|
+
&.continual
|
16
|
+
.animate-container
|
17
|
+
flex-direction: row
|
18
|
+
margin: 0 calc(-1 * var(--continual-gap) / 2)
|
19
|
+
transform: translateX(calc(var(--continual-gap) / 2))
|
20
|
+
.animate-item
|
21
|
+
padding: 0 calc(var(--continual-gap) / 2)
|
22
|
+
&[direction="top"],&[direction="bottom"]
|
23
|
+
&.continual
|
24
|
+
.animate-container
|
25
|
+
flex-direction: column
|
26
|
+
margin: calc(-1 * var(--continual-gap) / 2) 0
|
27
|
+
.animate-item
|
28
|
+
padding: calc(var(--continual-gap) / 2) 0
|
29
|
+
&[direction="left"]
|
30
|
+
&.continual
|
31
|
+
.animate-container
|
32
|
+
justify-content: flex-start
|
33
|
+
&[direction="right"]
|
34
|
+
&.continual
|
35
|
+
.animate-container
|
36
|
+
justify-content: flex-end
|
37
|
+
.animate-container
|
38
|
+
flex-wrap: nowrap
|
39
|
+
.animate-item
|
40
|
+
width: fit-content
|
41
|
+
opacity: 0
|
42
|
+
&.start,
|
43
|
+
&.start + .animate-item
|
44
|
+
opacity: 1
|
45
|
+
transition: opacity .6s
|