Hodnocení pomocí hvězdiček
K vytvoření tohoto výstupního formuláře musíte splnit několik kroků, obdobně jako u tvorby základního výstupního formuláře:
Vytvořit HTML dokument, přidat CSS styly a pomocí „JavaScript metod“ zajistit funkčnost ratingu a posílání dat z formuláře. Následně je nutné formulář nahrát na libovolný hosting podporující protokol HTTPS a přidat odkaz na formulář do administračního rozhraní aplikace Mluvii.
Doporučujeme vytvářet výstupní formulář pomocí webové aplikace codepen.io kvůli jednoduchosti a přehlednosti. Začít můžete kliknutím na tento odkaz, kde je již přednastaven ui framework Bootstrap a malá library simplebar, kvůli těžkostem se scrollováním iframe na systémech iOS. Na konci tohoto návodu můžete nalézt kompletní kód výstupního formuláře.
Jak bude formulář vypadat
Přímý odkaz pro vložení formuláře do administrace aplikace Mluvii: https://mluvii.github.io/customization/feedback_stars.html
HTML
První dva div tagy (a na ně napojené css a javascript) slouží k zobrazení vlastních scrollbarů pro lepší user experience na mobilních zařízeních.
Kromě inputů formuláře, HTML kód obsahuje svg obrázky ratingových hvězd. Lze je relativně snadno zaměnit za jiné obrazce, nutné je však dodržet pojmenování tříd a případně poupravit javascript kód, který s obrazci pracuje.
CSS Styling
Styl formuláře jsme upravili do barev aplikace Mluvii.
JavaScript kód
Javascript kód formuláře se stará o funkčnost hodnocení hvězdami a o poslání dat z formuláře do aplikace Mluvii. Hodnocení je posíláno skrze property "stars" ve "FEEDBACK_ACTION" zprávě.
Úpravy formuláře
Formulář můžete libovolně upravovat. Jednodušše například barvy. V CSS upravte korespondující proměnné v ":root" elementu, případně také v javascript kódu hned první dvě proměnné pro změnu barvy hvězd a v html kódu u polygon elementů atribut "fill" pro změnu základní barvy hvězd.
Veškeré možnosti API formuláře naleznete v obecném návodu na tvorbu výstupního formuláře.
Nahrát vytvořený formulář na libovolný hosting
V případě, že jste formulář vytvořili přes službu codepen.io, nejdříve klikněte na tlačítko Save, poté exportujte v .zip formátu (viz návod níže).
Tím máme připravený formulář. Nyní je potřeba nahrát formulář na internet, abychom na něho mohli odkazovat z administračního rozhraní. V našem případě využijeme GitHub stránky. Bližší informace naleznete zde.
V případě, že využijete jiný nástroj než codepen.io, bude kód formuláře vypadat takto:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.1/css/bootstrap.css" rel="stylesheet" />
<link rel="stylesheet" href="https://unpkg.com/simplebar@3.1.0/dist/simplebar.css" />
<style>
body {
color: #304558;
}
#my_form {
background-color: #eaf3f4;
}
.inner-container {
background-color: #fff;
}
input[type="email"], input[type="submit"], textarea, .inner-container {
border-radius: 0.5rem !important;
}
input[type="email"]:focus, textarea:focus {
border: 1px solid #4a8299 !important;
box-shadow: 0 0 0 2px #4a8299 !important;
}
input[type="submit"] {
background-color: #4a8299;
box-sizing: border-box;
border: none;
}
input[type="submit"]:hover, input[type="submit"]:focus, input[type="submit"]:active {
box-shadow: none !important;
}
input[type="submit"]:hover {
background-color: #5fadcc !important;
}
input[type="submit"]:active {
background-color: #35687c !important;
}
.curp {
cursor: pointer;
}
#rating, #handler {
height: 36px;
}
.icon {
height: 30px;
z-index: 2;
}
.rating-options {
flex: 1 1 auto;
padding: 0 4px;
z-index: 2;
align-items: center;
}
#rating-age {
position: absolute;
top: 50%;
left: 1rem;
content: '';
height: 2px;
position: absolute;
width: calc(100% - 2rem);
background-color: #E9ECEE;
}
#handler {
left: 0;
z-index: 3;
}
.feedback {
border: 1px solid #ced4da !important;
border: none;
margin-top: 15px;
}
#email_checkbox {
line-height: 1.9;
}
#email_checkbox:before, #email_checkbox:after {
height: 1.5rem;
width: 1.5rem;
left: -30px;
box-shadow: none !important;
}
.custom-checkbox {
padding-left: 30px;
}
.custom-control-input:checked ~ .custom-control-label::before {
background-color: #4a8299 !important;
}
.custom-control-input:active ~ .custom-control-label::before {
background-color: #9ecee2 !important;
}
.scroll-container {
max-height: 107vh;
width: 1px !important;
min-width: 100% !important;
max-width: 100% !important;
}
.scroll {
max-width: 100% !important;
}
</style>
</head>
<body>
<div class="scroll-container">
<div class="scroll" data-simplebar>
<form id="my_form" class="p-2 m-2">
<p class="text-center mt-3 mb-3 font-weight-bold">Jak jste s námi byli spokojeni?</p>
<div class="inner-container px-3 pt-4 pb-2">
<div id="rating" class="rating d-flex justify-content-center mx-auto position-relative">
<div id="rating_options" class="rating-options d-flex justify-content-between">
<label class="icon curp mb-0">
<svg width="30px" viewBox="0 0 115.4 113.4">
<circle class="body" fill="#C6CCD0" data-fill="#7bca71" cx="57.7" cy="56.7" r="56.7" />
<path class="mouth" fill="#FFFFFF" d="M57.8,70.7c-6.4,0-19.4,0-27.8,0c-1.9,0-3.2,1-4,2.3c-0.9,1.5-0.9,3.4,0.3,5c7.1,9.6,18.5,15.7,31.4,15.7c12.8,0,24.2-6.2,31.4-15.7c1.1-1.5,1.1-3.3,0.4-4.7c-0.7-1.5-2.3-2.6-4.2-2.6C79.1,70.7,66.1,70.7,57.8,70.7z" />
<path class="right-eye" fill="#FFFFFF" d="M70.7,38c0.3-0.5,0.7-1,1.2-1.5c1.9-1.9,4.5-2.8,7-2.7c1.9,0.1,3.8,0.8,5.3,2c0.3,0.2,0.5,0.4,0.8,0.7c2.2,2.2,3,5.2,2.6,8c-0.3,1.8-1.2,3.6-2.6,5c-3.6,3.6-9.4,3.6-13,0C68.7,46.4,68.3,41.5,70.7,38z" />
<path class="left-eye" fill="#FFFFFF" d="M43.4,49.5c3.1-3.1,3.5-8,1.2-11.6c-0.3-0.5-0.7-1-1.2-1.5c-1.9-1.9-4.5-2.8-7-2.7c-1.9,0.1-3.8,0.8-5.3,2c-0.3,0.2-0.5,0.4-0.8,0.7c-2.2,2.2-3,5.2-2.6,8c0.3,1.8,1.2,3.6,2.6,5C34,53.1,39.8,53.1,43.4,49.5z" />
</svg>
<input
value="5"
type="radio"
id="rating_0"
name="rating"
class="d-none"
checked="true"
/>
</label>
<label class="icon curp mb-0">
<svg width="30px" viewBox="0 0 115.4 113.4">
<circle class="body" fill="#C6CCD0" data-fill="#acd567" cx="57.7" cy="56.7" r="56.7" />
<path class="mouth" fill="#FFFFFF" d="M57.6,86.7c8.5,0,16.7-2.2,23.9-6.3c2.2-1.3,3-4.1,1.7-6.3c-1.3-2.2-4.1-3-6.3-1.7c-5.9,3.3-12.5,5.1-19.4,5.1s-13.5-1.8-19.4-5.1c-2.2-1.3-5-0.5-6.3,1.7c-1.3,2.2-0.5,5,1.7,6.3C40.9,84.5,49.1,86.7,57.6,86.7z"/>
<path class="right-eye" fill="#FFFFFF" d="M70.7,38c0.3-0.5,0.7-1,1.2-1.5c1.9-1.9,4.5-2.8,7-2.7c1.9,0.1,3.8,0.8,5.3,2c0.3,0.2,0.5,0.4,0.8,0.7c2.2,2.2,3,5.2,2.6,8c-0.3,1.8-1.2,3.6-2.6,5c-3.6,3.6-9.4,3.6-13,0C68.7,46.4,68.3,41.5,70.7,38z"/>
<path class="left-eye" fill="#FFFFFF" d="M43.4,49.5c3.1-3.1,3.5-8,1.2-11.6c-0.3-0.5-0.7-1-1.2-1.5c-1.9-1.9-4.5-2.8-7-2.7c-1.9,0.1-3.8,0.8-5.3,2c-0.3,0.2-0.5,0.4-0.8,0.7c-2.2,2.2-3,5.2-2.6,8c0.3,1.8,1.2,3.6,2.6,5C34,53.1,39.8,53.1,43.4,49.5z"/>
</svg>
<input
value="4"
type="radio"
id="rating_1"
name="rating"
class="d-none"
/>
</label>
<label class="icon curp mb-0">
<svg width="30px" viewBox="0 0 115.4 113.4">
<circle class="body" fill="#C6CCD0" data-fill="#fcdb5b" cx="57.7" cy="56.7" r="56.7" />
<path class="mouth" fill="#FFFFFF" d="M57.7,74.7c-5.9,1-11.6,2.3-17.4,3.5c-2.5,0.6-4,3-3.5,5.5c0.6,2.5,3,4,5.5,3.5c5.7-1.3,11.2-2.4,17-3.5c5.8-1.1,11.7-2,17.6-2.9c2.5-0.4,4.3-2.7,3.9-5.2c-0.4-2.5-2.7-4.3-5.2-3.9c-6,0.9-12,1.8-35.4,6.5L57.7,74.7z"/>
<path class="right-eye" fill="#FFFFFF" d="M70.7,38c0.3-0.5,0.7-1,1.2-1.5c1.9-1.9,4.5-2.8,7-2.7c1.9,0.1,3.8,0.8,5.3,2c0.3,0.2,0.5,0.4,0.8,0.7c2.2,2.2,3,5.2,2.6,8c-0.3,1.8-1.2,3.6-2.6,5c-3.6,3.6-9.4,3.6-13,0C68.7,46.4,68.3,41.5,70.7,38z"/>
<path class="left-eye" fill="#FFFFFF" d="M43.4,49.5c3.1-3.1,3.5-8,1.2-11.6c-0.3-0.5-0.7-1-1.2-1.5c-1.9-1.9-4.5-2.8-7-2.7c-1.9,0.1-3.8,0.8-5.3,2c-0.3,0.2-0.5,0.4-0.8,0.7c-2.2,2.2-3,5.2-2.6,8c0.3,1.8,1.2,3.6,2.6,5C34,53.1,39.8,53.1,43.4,49.5z"/>
</svg>
<input
value="3"
type="radio"
id="rating_2"
name="rating"
class="d-none"
/>
</label>
<label class="icon curp mb-0">
<svg width="30px" viewBox="0 0 115.4 113.4">
<circle class="body" fill="#C6CCD0" data-fill="#ffb94f" cx="57.7" cy="56.7" r="56.7" />
<path class="mouth" fill="#FFFFFF" d="M57.6,73c-7.5,0-14.5,1.3-22.3,4c-2.4,0.8-3.7,3.5-2.8,5.9c0.8,2.4,3.5,3.7,5.9,2.8c6.8-2.4,12.8-3.5,19.2-3.5c7.2,0,15,1.4,18.6,3.2c2.3,1.2,5,0.3,6.2-1.9c1.2-2.3,0.3-5-1.9-6.2C75.4,74.6,66.1,73,57.6,73z"/>
<path class="right-eye" fill="#FFFFFF" d="M70.7,38c0.3-0.5,0.7-1,1.2-1.5c1.9-1.9,4.5-2.8,7-2.7c1.5,1.8,1.6,2,3,3.7c1.4,1.7,1.4,1.8,2.5,3c1,1.3,1.6,1.9,3.2,4c-0.3,1.8-1.2,3.6-2.6,5c-3.6,3.6-9.4,3.6-13,0C68.8,46.4,68.4,41.5,70.7,38z"/>
<path class="left-eye" fill="#FFFFFF" d="M43.4,49.5c3.1-3.1,3.5-8,1.2-11.6c-0.3-0.5-0.7-1-1.2-1.5c-1.9-1.9-4.5-2.8-7-2.7c-1.5,1.8-1.6,2-3,3.7c-1.4,1.7-1.4,1.8-2.5,3c-1,1.3-1.6,1.9-3.2,4c0.3,1.8,1.2,3.6,2.6,5C33.9,53.1,39.8,53.1,43.4,49.5z"/>
</svg>
<input
value="2"
type="radio"
id="rating_3"
name="rating"
class="d-none"
/>
</label>
<label class="icon curp mb-0">
<svg width="30px" viewBox="0 0 115.4 113.4">
<circle class="body" fill="#C6CCD0" data-fill="#ff7b48" cx="57.7" cy="56.7" r="56.7" />
<path class="mouth" fill="#FFFFFF" d="M57.3,71.5c-8.5,0-16.6,2.2-23.8,6.3c-2.2,1.3-3,4.1-1.7,6.3c1.3,2.2,4.1,3,6.3,1.7c5.8-3.3,12.4-5.1,19.3-5.1s13.5,1.8,19.3,5.1c2.2,1.3,5,0.5,6.3-1.7c1.3-2.2,0.5-5-1.7-6.3C74,73.7,65.8,71.5,57.3,71.5z" />
<path class="right-eye" fill="#FFFFFF" d="M70.3,37.8c1.2-0.2,1.4-0.3,2.6-0.5c1.2-0.2,4.3-0.6,6.4-1c2.1-0.4,2.6-0.5,4.4-0.7c0.3,0.2,0.5,0.4,0.8,0.7c2.2,2.2,3,5.2,2.6,8c-0.3,1.8-1.2,3.6-2.6,5c-3.6,3.6-9.4,3.6-13,0C68.4,46.2,68,41.4,70.3,37.8z" />
<path class="left-eye" fill="#FFFFFF" d="M43.2,49.3c3.1-3.1,3.5-7.9,1.2-11.5c-1.2-0.2-1.4-0.3-2.6-0.5c-1.2-0.2-4.3-0.6-6.4-1c-2.1-0.4-2.6-0.5-4.4-0.7c-0.3,0.2-0.5,0.4-0.8,0.7c-2.2,2.2-3,5.2-2.6,8c0.3,1.8,1.2,3.6,2.6,5C33.8,52.9,39.6,52.9,43.2,49.3z" />
</svg>
<input
value="1"
type="radio"
id="rating_4"
name="rating"
class="d-none"
/>
</label>
</div>
<div id="rating-age"></div>
<div id="handler" class="curp position-absolute">
<svg width="40px" viewBox="0 0 115.4 113.4">
<circle fill="#8EC254" class="body" cx="57.7" cy="56.7" r="56.7" />
<path class="mouth" fill="#31464e" d="M57.8,70.7c-6.4,0-19.4,0-27.8,0c-1.9,0-3.2,1-4,2.3c-0.9,1.5-0.9,3.4,0.3,5c7.1,9.6,18.5,15.7,31.4,15.7c12.8,0,24.2-6.2,31.4-15.7c1.1-1.5,1.1-3.3,0.4-4.7c-0.7-1.5-2.3-2.6-4.2-2.6C79.1,70.7,66.1,70.7,57.8,70.7z" />
<path class="right-eye" fill="#31464e" d="M70.7,38c0.3-0.5,0.7-1,1.2-1.5c1.9-1.9,4.5-2.8,7-2.7c1.9,0.1,3.8,0.8,5.3,2c0.3,0.2,0.5,0.4,0.8,0.7c2.2,2.2,3,5.2,2.6,8c-0.3,1.8-1.2,3.6-2.6,5c-3.6,3.6-9.4,3.6-13,0C68.7,46.4,68.3,41.5,70.7,38z" />
<path class="left-eye" fill="#31464e" d="M43.4,49.5c3.1-3.1,3.5-8,1.2-11.6c-0.3-0.5-0.7-1-1.2-1.5c-1.9-1.9-4.5-2.8-7-2.7c-1.9,0.1-3.8,0.8-5.3,2c-0.3,0.2-0.5,0.4-0.8,0.7c-2.2,2.2-3,5.2-2.6,8c0.3,1.8,1.2,3.6,2.6,5C34,53.1,39.8,53.1,43.4,49.5z" />
</svg>
</div>
</div>
<textarea
rows="4"
id="feedback"
name="feedback"
class="form-control feedback"
placeholder="Vaše zpětná vazba"
></textarea>
<div class="custom-control custom-checkbox mt-3 mb-2">
<input type="checkbox" class="custom-control-input" id="send_to_email">
<label id="email_checkbox" class="custom-control-label curp" for="send_to_email">
Chci poslat přepis konverzace
</label>
</div>
<div class="form-group">
<input
disabled
required
id="email"
type="email"
placeholder="E-mailová adresa"
class="form-control email"
/>
</div>
</div>
<div class="form-group d-flex py-3 m-0">
<input
id="submit"
type="submit"
value="Ok"
class="btn btn-primary mt-3 mx-auto px-5"
/>
</div>
</form>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.1/js/bootstrap.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/flubber/0.4.2/flubber.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/latest/TweenMax.min.js"></script>
<script src="https://unpkg.com/simplebar@3.1.0/dist/simplebar.js"></script>
<script>
// Variables
var handlerStartOffset = 0;
var handleDragStartOffset = 0;
var handler = document.getElementById('handler');
var ratingContainer = document.getElementById('rating');
var ratingContainerWidth = ratingContainer.offsetWidth;
var ratingOptionsContainer = document.getElementById('rating_options');
var icons = [].slice.call(ratingOptionsContainer.querySelectorAll('.icon'));
icons = icons.map(function(el, i) {
return {
element: el,
offsetLeft: el.offsetLeft,
bodyColor: el.querySelector('.body').getAttribute('data-fill'),
mouth: el.querySelector('.mouth').getAttribute('d'),
leftEye: el.querySelector('.left-eye').getAttribute('d'),
rightEye: el.querySelector('.right-eye').getAttribute('d')
};
});
// Animation timeline.
var tl = new TimelineMax({ paused: true });
icons.forEach(function(icon, i) {
icon.element.addEventListener('click', function() {
tl.tweenTo('rating_' + i, {
ease: Expo.easeOut
}).duration(.6);
});
var iconMinimization = new TweenLite.to(icon.element, .6, {
scale: 0
});
var body = handler.querySelector('.body');
var mouth = handler.querySelector('.mouth');
var leftEye = handler.querySelector('.left-eye');
var rightEye = handler.querySelector('.right-eye');
var handlePosition = new TweenLite.to(handler, .6, {
left: icon.offsetLeft - 4,
onUpdate: function() {
var propgress = handlePosition.progress();
if (i !== 0) {
var ic = icons[i - 1];
var m = flubber.interpolate(ic.mouth, icon.mouth);
var l = flubber.interpolate(ic.leftEye, icon.leftEye);
var r = flubber.interpolate(ic.rightEye, icon.rightEye);
mouth.setAttribute('d', m(propgress));
leftEye.setAttribute('d', l(propgress));
rightEye.setAttribute('d', r(propgress));
}
},
ease: Power0.easeNone
});
var label = 'rating_' + i;
var prevLabel = 'rating_' + (i - 1);
handlePosition.data = {
index: i,
label: label,
prevLabel: prevLabel
};
var handlerColor = new TweenLite.to(body, .6, {
fill: icon.bodyColor
});
if (i !== 0) {
var iconMaximization = new TweenLite.to(icons[i - 1].element, .6, {
scale: 1
});
}
tl.add(handlePosition)
.addLabel(label)
.add(handlerColor, '-=.6')
.add(iconMinimization, '-=.6');
if (iconMaximization) {
tl.add(iconMaximization, '-=.6');
}
});
function autocomplete(e) {
var label;
var curLabel = tl.currentLabel();
var nextLabel = tl.getLabelAfter();
var curLabelTime = tl.getLabelTime(curLabel);
var nextLabelTime = tl.getLabelTime(nextLabel);
var halfWay = curLabelTime + (nextLabelTime - curLabelTime) / 2 < tl.time();
label = halfWay ? nextLabel || curLabel : curLabel;
if (label) {
tl.tweenTo(label);
document.getElementById(label).checked = true;
}
};
handler.addEventListener("mousedown", function(e) {
document.body.addEventListener("mousemove", handleMouseMove);
handleDragStartOffset = e.clientX + 15;
handlerStartOffset = parseInt(handler.style.left) || 0;
});
document.body.addEventListener("mouseup", function(e) {
if (handleDragStartOffset) {
handleDragStartOffset = null;
document.body.removeEventListener("mousemove", handleMouseMove);
autocomplete();
}
});
function handleMouseMove(e) {
var offsetX = e.clientX - handleDragStartOffset + 48 + 48 / 2;
var newProgress = Math.max(0, (offsetX + handlerStartOffset) / (ratingContainerWidth + 20));
tl.progress(newProgress);
};
// Form
var form = document.getElementById('my_form');
var submit = document.getElementById('submit');
var checkbox = document.getElementById('send_to_email');
checkbox.onchange = function(e) {
document.getElementById('email').disabled = !e.target.checked;
};
form.onsubmit = function (e) {
e.preventDefault();
var email = document.getElementById('email').value;
var feedback = document.getElementById('feedback').value;
var checked = document.getElementById('send_to_email').checked;
var rating = document.querySelector('input[type="radio"]:checked');
window.parent.postMessage({
type: "FEEDBACK_ACTION",
email: checked ? email : null,
stars: rating.value,
content: feedback
}, "*");
};
</script>
</body>
</html>
Vložení odkazu do administrace aplikace mluvii
Vytvořili jste vstupní formulář pomocí codepen.io. Ten jste exportovali na Váš počítač. Exportované složky jste nahráli do GitHub repozitáře a poté zapnuli GitHub stránky. Tím pádem máte odkaz na náš formulář, který vložíte do administračního rozhraní.
Last updated