add photo placeholder, mirrored camera capture, configurable footer, and student .url zip export
This commit is contained in:
+31
-10
@@ -15,15 +15,15 @@ h1{font-size:1.5rem;font-weight:600;margin-bottom:8px;letter-spacing:-.02em;text
|
||||
@media(max-width:760px){.form{grid-template-columns:1fr;max-width:480px}}
|
||||
|
||||
.photo-col{display:flex;flex-direction:column;gap:16px}
|
||||
.photo-card{background:var(--card);border:1px solid var(--border);border-radius:var(--radius);padding:24px;display:flex;flex-direction:column;gap:16px;align-items:center}
|
||||
.photo-card{border-radius:var(--radius);padding:24px;display:flex;flex-direction:column;gap:16px;align-items:center}
|
||||
.photo-btns{display:flex;flex-direction:column;gap:12px;width:100%}
|
||||
.photo-btns button{width:100%;border-radius:var(--radius);padding:14px 16px;font-size:.95rem;font-weight:600;cursor:pointer;border:1px solid var(--border);background:var(--card);color:var(--text);transition:border .15s,background .15s}
|
||||
.photo-btns button:hover{border-color:var(--accent)}
|
||||
.photo-btns .cam{background:var(--accent);color:#fff;border-color:var(--accent)}
|
||||
.photo-btns .cam:hover{opacity:.85}
|
||||
.preview-wrap{width:100%;display:flex;flex-direction:column;align-items:center;gap:8px}
|
||||
.preview{max-width:100%;width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:var(--radius);background:var(--bg);display:none}
|
||||
.preview.show{display:block}
|
||||
.preview{max-width:100%;width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:var(--radius);background:var(--card);display:block;border:1px dashed var(--border)}
|
||||
.preview.has-photo{border-style:solid}
|
||||
.remove-photo{background:none;border:none;color:#ef4444;font-size:.85rem;cursor:pointer;padding:4px;display:none}
|
||||
.remove-photo.show{display:block}
|
||||
|
||||
@@ -39,6 +39,11 @@ textarea{resize:vertical;min-height:160px;font-family:inherit;line-height:1.5}
|
||||
.btn:hover{opacity:.85}
|
||||
.btn:disabled{opacity:.4;cursor:not-allowed}
|
||||
|
||||
.footer{max-width:900px;margin:48px auto 0;padding-top:24px;border-top:1px solid var(--border);display:flex;justify-content:space-between;gap:16px;font-size:.85rem;color:var(--muted)}
|
||||
.footer .f-left{flex:1;text-align:left}
|
||||
.footer .f-right{flex:1;text-align:right}
|
||||
@media(max-width:760px){.footer{flex-direction:column;text-align:center}.footer .f-left,.footer .f-right{text-align:center}}
|
||||
|
||||
.toast{position:fixed;bottom:24px;left:50%;transform:translateX(-50%);background:#16a34a;color:#fff;padding:12px 24px;border-radius:var(--radius);font-size:.9rem;opacity:0;transition:opacity .3s;pointer-events:none;z-index:200}
|
||||
.toast.show{opacity:1}
|
||||
.toast.error{background:#dc2626}
|
||||
@@ -69,13 +74,13 @@ nav a:hover{color:var(--text)}
|
||||
<form class="form" id="form">
|
||||
<div class="photo-col">
|
||||
<div class="photo-card">
|
||||
<div class="preview-wrap">
|
||||
<img class="preview" id="preview" alt="Фото" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='640' height='480'%3E%3Crect width='100%25' height='100%25' fill='none' stroke='%23475569' stroke-width='2' stroke-dasharray='8 8'/%3E%3Ctext x='50%25' y='50%25' fill='%2364748b' font-family='sans-serif' font-size='28' text-anchor='middle' dominant-baseline='middle'%3EФото%3C/text%3E%3C/svg%3E">
|
||||
<button type="button" class="remove-photo" id="removePhotoBtn" onclick="removePhoto()">Убрать фото</button>
|
||||
</div>
|
||||
<div class="photo-btns">
|
||||
<button type="button" class="cam" onclick="openCam()">Камера</button>
|
||||
</div>
|
||||
<div class="preview-wrap">
|
||||
<img class="preview" id="preview">
|
||||
<button type="button" class="remove-photo" id="removePhotoBtn" onclick="removePhoto()">Убрать фото</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -102,6 +107,11 @@ nav a:hover{color:var(--text)}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="f-left" id="footerLeft"></div>
|
||||
<div class="f-right" id="footerRight"></div>
|
||||
</footer>
|
||||
|
||||
<div class="modal" id="camModal">
|
||||
<div class="modal-box">
|
||||
<video id="camVideo" autoplay playsinline></video>
|
||||
@@ -119,6 +129,7 @@ nav a:hover{color:var(--text)}
|
||||
const API = location.origin;
|
||||
const form = document.getElementById('form');
|
||||
const preview = document.getElementById('preview');
|
||||
const placeholderSrc = preview.src;
|
||||
const removePhotoBtn = document.getElementById('removePhotoBtn');
|
||||
const groupInput = document.getElementById('groupInput');
|
||||
const toast = document.getElementById('toast');
|
||||
@@ -165,16 +176,23 @@ if (params.get('nameInput')) {
|
||||
groupInput.innerHTML = '<option value="" disabled selected>Нет активных групп</option>';
|
||||
}
|
||||
}
|
||||
try {
|
||||
const fRes = await fetch(`${API}/api/public-settings`);
|
||||
const f = await fRes.json();
|
||||
if (f.footer_left) document.getElementById('footerLeft').textContent = f.footer_left;
|
||||
if (f.footer_right) document.getElementById('footerRight').textContent = f.footer_right;
|
||||
} catch (e) { /* ignore */ }
|
||||
})();
|
||||
|
||||
function showPreview(src) {
|
||||
preview.src = src;
|
||||
preview.classList.add('show');
|
||||
preview.classList.add('has-photo');
|
||||
removePhotoBtn.classList.add('show');
|
||||
}
|
||||
|
||||
function removePhoto() {
|
||||
preview.classList.remove('show');
|
||||
preview.src = placeholderSrc;
|
||||
preview.classList.remove('has-photo');
|
||||
removePhotoBtn.classList.remove('show');
|
||||
capturedBlob = null;
|
||||
}
|
||||
@@ -197,7 +215,10 @@ function closeCam() {
|
||||
function capture() {
|
||||
camCanvas.width = camVideo.videoWidth;
|
||||
camCanvas.height = camVideo.videoHeight;
|
||||
camCanvas.getContext('2d').drawImage(camVideo, 0, 0);
|
||||
const ctx = camCanvas.getContext('2d');
|
||||
ctx.translate(camCanvas.width, 0);
|
||||
ctx.scale(-1, 1);
|
||||
ctx.drawImage(camVideo, 0, 0);
|
||||
camCanvas.toBlob(blob => {
|
||||
capturedBlob = blob;
|
||||
showPreview(URL.createObjectURL(blob));
|
||||
|
||||
Reference in New Issue
Block a user