D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
EE2dev
Full window
Github gist
Math test
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <style> div.main{ min-width: 970px; padding: 0; margin: 0; } div.left { vertical-align: top; display: inline-block; width: 645px; height: 720px; padding: 1.25em; } div.right { vertical-align: top; display: inline-block; width: 200px; height: 720px; padding: 1.25em; } div.right1, div.right2 { height: 300px; padding: 0px; margin: 0px; } footer { font-size: 12px; padding: 1.25em; margin-left: 33px; } div.answer { position: relative; left: 0px; height: 600px; text-align: center; margin-top: 20px; margin-left: 20px; } div.question, div.feedback { font-size: 36px; text-align: center; margin-bottom: 5px; background-color: white; /* outer shadows (note the rgba is red, green, blue, alpha) */ -webkit-box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.4); -moz-box-shadow: 0px 1px 6px rgba(23, 69, 88, .5); /* rounded corners */ -webkit-border-radius: 12px; -moz-border-radius: 7px; border-radius: 7px; /* gradients */ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, white), color-stop(15%, white), color-stop(100%, #D7E9F5)); background: -moz-linear-gradient(top, white 0%, white 55%, #D5E4F3 130%); } div.result, div.menu { font-size: 16px; font-weight: bold; text-align: center; } div.resultsCorrect { background-color: #5ab4ac; background-color: #a1d99b; } div.resultsIncorrect { background-color: #fee0d2; } circle { fill-opacity: 0.7; stroke: black; } .line { stroke: grey; } text.number { font-size: 17px; } .number { pointer-events: none; } </style> <script src="https://d3js.org/d3.v3.js"></script> </head> <body> <div class="main"> <div class="left"> <div class="question"></div> <div class="feedback"></div> <div class="answer"></div> </div> <div class="right"> <div class="right1"> <div class="menu"></div> <p> <select id ="sp" name="language" onchange="setLanguage(this.value, true);"> <option value="english" selected>English</option> <option value="french">Français</option> <option value="spanish">Español</option> <option value="german">Deutsch</option> <option value="hebrew">עברי</option> <option value="arabic">العربية</option> </select> </p> <p> <select id ="ro" name="operations" onchange="reset()"> <option value="+" selected>+ Addition</option> <option value="-">- Subtraktion</option> <option value="*">\xB7 Multiplikation</option> <option value=":">: Division</option> </select> </p> <p> <input type="checkbox" id="cbox1" value="first_checkbox" onchange="checkTime()"><label class="time">Zeitmessung (10 Fragen)</label> </p> <p> <input checked="true" type="checkbox" id="cbox2" value="second_checkbox"><label class="sound">Ton</label> </p> </div> <div class="right2"> <div class="result"></div> <p class="timer"></p> <p class="rightAnswers"></p> <p class="wrongAnswers"></p> </div> </div> </div> <footer> <p>©2016 ee2.dev@gmail.com - page best viewed with Google Chrome. </p> </footer> <script> 'use strict' // evaluate answers var totalCorrect = 0; var totalQuestions = 0; var correctAnswers = []; var incorrectAnswers = []; var data; // data: results for forced layout var value1, value2, result; // values for question + solution // translate Object for language specific text // properties: calcOptions, timeString, positiveComment, negativeComment, infoComment, resultComment, // soundLabel, correctResults, incorrectResults, timeResults; var translate = {}; var counter = 0; var myTimer; var selectedIndex; var indexOf; // d3.map. map value -> index of node var nodes; var links; var nodesRemoved; var linksRemoved; var force = d3.layout.force(); var chargeValue = -220; // variables for the SVG var svgHeight = 600; var svgWidth = 600; var fontSize = 20; var radius = 15, cx = 0, cy = 0; var colors = d3.scale.category10(); // to distinguish between drag and click // Workaround because of fired mousemove events https://bugs.chromium.org/p/chromium/issues/detail?id=161464 var mousemove = 0; var dragFlag = false; var svg = d3.select("div.answer") .append("svg") .attr("style", "outline: thin solid steelblue;") .attr("height", svgHeight) .attr("width", svgWidth); reset(); function reset() { var questionType = d3.select("select#ro").node().value; newQuestion(questionType); draw(data); setLanguage(d3.select("select#sp").node().value, true); checkTime(); } function newQuestion(questionType, numberRange) { var maxValue; d3.select("div.question") .style("background", "rgba(0, 0, 0, 0) -webkit-gradient(linear, 0% 0%, 0% 100%, from(white), color-stop(0.15, white), to(rgb(215, 233, 245))) repeat scroll 0% 0% / auto padding-box border-box"); if (!questionType) { questionType = d3.select("#ro").node().value;} switch (questionType) { case "+": data = d3.range(0, 100, 1); result = getRandomInteger(data[0],data[data.length-1]); value2 = getRandomInteger(data[0], result); value1 = result - value2; break; case "-": data = d3.range(0, 100, 1); value1 = getRandomInteger(data[0],data[data.length-1]); value2 = getRandomInteger(data[0], value1); result = value1 - value2; break; case "*": data = d3.range(0, 10, 1); value1 = getRandomInteger(data[0],data[data.length-1]); value2 = getRandomInteger(data[0],data[data.length-1]); result = value1 * value2; data = d3.range(0, 100, 1); break; case ":": data = d3.range(0, 10, 1); result = getRandomInteger(data[0],data[data.length-1]); value2 = getRandomInteger(data[1],data[data.length-1]); value1 = value2 * result; break; } questionType = (questionType === "*") ? "\xB7" : questionType; d3.select("div.question") .text(value1 + " " + questionType + " " + value2 + " = "); } // functions for drawing the SVG of the answer div function draw(_data) { nodes = []; links = []; selectedIndex = -1; nodesRemoved = []; linksRemoved = []; svg.selectAll("g.node-objects").remove(); createNodesAndLinks(_data); setupForceLayout(); drawNodes(); } function createNodesAndLinks(_data) { var startingPoint = [350, 700]; indexOf = d3.map(); for (var i = 0; i < _data.length; ++i) { nodes.push({ x: startingPoint[0] + Math.random() * 100, y: startingPoint[1] + Math.random() * 100, value: _data[i] }); indexOf.set(nodes[nodes.length-1].value, i); if (i % 10 !== 0) { // add links to predecessor for nodes except for 0,10,20,... links.push({source: nodes[i-1], target: nodes[i]}); if (i % 10 === 9) { // add links to create circular graph for 10's, 20s,.. links.push({source: nodes[i-9], target: nodes[i]}); } } } } function setupForceLayout() { force.nodes(nodes) .size([svgWidth, svgHeight]) .links(links) .gravity(0.1) .charge( function(d) { return (d.index === selectedIndex) ? chargeValue : -80;}) .friction(0.95) .linkDistance(30) .linkStrength( function(d) { return (d.index === selectedIndex) ? 0 : 1;}); force.on("tick", function () { svg.selectAll("g.node-objects") .filter( function(d) { return nodesRemoved.indexOf(d.index ) === -1; }) .attr("transform", function (d) {return "translate (" + d.x + ", " + d.y +")";}); }); force.start(); } function drawNodes() { var nodeObjects = svg.selectAll("g.node-objects") .data(nodes) .enter() .append("g") .attr("class", "node-objects") .attr("transform", "translate (-50, -50)") // invisible start position .on("mousedown", function (d) { mousemove = 0; dragFlag = false; }) .on("mousemove", function (d) { mousemove++; if (mousemove > 1) dragFlag = true; }) .on("mouseup", function (d) { if (dragFlag) {return;} var transX = d3.transform(d3.select(this).attr("transform")).translate[0]; var transY = d3.transform(d3.select(this).attr("transform")).translate[1]; playSound("blow.mp3"); d3.select(this) .each(function(d) { selectedIndex = indexOf.get(d.value); nodes[selectedIndex].fixed = true; }) .transition() .duration(1000) .ease("circle") .attr("transform", "translate(" + transX + ", " + transY +") scale(3)") .each("end", evaluate); // detach links from selected node by self reference. // Otherwise index shifts would have to be taken care of if (selectedIndex === 0) { force.links()[selectedIndex] = {source: nodes[selectedIndex], target: nodes[selectedIndex]}; } else { force.links()[selectedIndex-1] = {source: nodes[selectedIndex-1], target: nodes[selectedIndex-1]}; force.links()[selectedIndex] = {source: nodes[selectedIndex], target: nodes[selectedIndex]}; } nodesRemoved.push(selectedIndex); force.start(); // update charge for selected node }) .call(force.drag); nodeObjects.append("circle") .attr("class", "node") .attr("r", radius) .style("fill", function (d, i) { return colors(Math.ceil((i+1)/10));}); nodeObjects.append("text") .attr("class", "number") .attr("x", function(d) { if (d.value > 99) { return - fontSize*0.7; } else if (d.value > 9) { return - fontSize*0.5; } else { return - fontSize * 0.25; }}) .attr("y", function(d) { return - fontSize * 0.35;}) .attr("dy", ".71em") .text(function(d, i) { return d.value;}); } function evaluate(d, i) { var guess = d.index; totalQuestions++; if (totalQuestions === 10) { clearInterval(myTimer); } var feedback = d3.select("div.feedback") feedback.select("h1").remove(); if (guess === result) { totalCorrect++; correctAnswers.push(d3.select("div.question").text() + guess); d3.select("div.question") .style("background", "none") .style("background-color", "lightgreen") .text(correctAnswers[correctAnswers.length-1]); showSuccess(); feedback.html(function() { return translate.positiveComment[getRandomInteger(0, translate.positiveComment.length-1)]}) .transition() .duration(2000) .each("end", newQuestion); d3.select("div.result").text(translate.resultComment + ": " + totalCorrect + "/" + totalQuestions); } else { incorrectAnswers.push(d3.select("div.question").text() + guess); feedback.html(function() { return translate.negativeComment[getRandomInteger(0, translate.negativeComment.length-1)]}); burstCircle(); d3.select("div.result").text(translate.resultComment + ": " + totalCorrect + "/" + totalQuestions); } showResults(); } function burstCircle() { console.log("fail"); playSound("burst.mp3"); var selectedCircle = d3.selectAll("circle") .filter (function(d){return d.index === selectedIndex;}); selectedCircle.transition() .duration(100) .style("fill-opacity", 0) .style("stroke", "none") .each("end", dropNumber); force.stop(); chargeValue = -10000; force.start(); } function dropNumber() { force.stop(); chargeValue = -220; force.start(); var yPos = force.nodes()[selectedIndex].y; d3.selectAll("circle.node") .filter (function(d){return d.index === selectedIndex;}) .remove(); var selectedText = d3.selectAll("text.number") .filter (function(d){return d.index === selectedIndex;}) selectedText.transition() .duration(2000) .ease("bounce") .attr("y", (svgHeight - fontSize*0.35 - yPos)/3 - 10); } function showSuccess() { console.log("success"); playSound("cheer.mp3"); force.stop(); var circles = d3.selectAll("circle.node") .filter (function(d){return d.index !== selectedIndex;}); circles.transition() .duration(1000) .style("fill-opacity", 0) .style("stroke-opacity", 0.1); var numbers = d3.selectAll("text.number") .filter (function(d){return d.index !== selectedIndex;}); numbers.transition() .duration(2000) .style("opacity", 0.1) .filter(function(d, i) { return (i === data.length - 2) ? true : false; }) .each("end", function(){ draw(data);}); } function showResults() { if (correctAnswers.length > 0){ d3.select("p.rightAnswers") .text(translate.correctResults) .selectAll("div.resultsCorrect") .data(correctAnswers) .enter() .append("div") .attr("class", "resultsCorrect") .text(function(d) {return d;}); } if (incorrectAnswers.length > 0){ d3.select("p.wrongAnswers") .text(translate.incorrectResults) .selectAll("div.resultsIncorrect") .data(incorrectAnswers) .enter() .append("div") .attr("class", "resultsIncorrect") .text(function(d) {return d;}); } } function playSound(soundfile) { if (document.getElementById("cbox2").checked) { var mp3 = new Audio(soundfile); mp3.play(); } } // function 1 for the timer function checkTime() { if (document.getElementById("cbox1").checked===false) { clearInterval(myTimer); d3.select("p.timer").text(""); d3.select("div.timer").remove(); } else { counter = 0; d3.select("p.timer") .text(translate.timeResults) .append("div") .attr("class", "timer"); myTimer = setInterval(countNow, 10); } } // function 2 for the timer function countNow() { counter += 0.01; d3.select("div.timer").text(counter.toFixed(2)); } function getRandomInteger(lower, upper) { if (arguments.length === 1) { upper = lower; lower = 0; } return Math.floor(Math.random() * (upper - lower + 1)) + lower; } function setLanguage(language, updateResult) { switch (language) { case "german": translate.calcOptions = ["+ Addition", "- Subtraktion", "\xB7 Multiplikation", ": Division"]; translate.timeString = "Zeitmessung (10 Fragen)"; translate.positiveComment = ["Bravo!", "Sehr gut!", "Prima!", "Na also, es geht doch!", "Ausgezeichnet!"]; translate.negativeComment = ["Nicht so schnell ... Versuch es noch einmal!", "Du Schlafmütze ... Mach das bitte nochmal!", "Leider falsch ...noch ein Versuch!", "Bist Du sicher? Schau nochmal genau hin!", "Falsch ... bitte jetzt aber richtig!"]; translate.infoComment = "Bitte klicke auf die richtige Antwort!"; translate.resultComment = "Ergebnis"; translate.menuTitle = "Menü"; translate.soundLabel = "Ton"; translate.correctResults = "Richtige Antworten:"; translate.incorrectResults = "Falsche Antworten:"; translate.timeResults = "Zeit:"; break; case "english": translate.calcOptions = ["+ summation", "- subtraction", "\xB7 multiplication", ": division"]; translate.timeString = "timer (10 questions)"; translate.positiveComment = ["Great!", "Very good!", "Excellent!", "I told you, you can do it!", "Terrific!"]; translate.negativeComment = ["Not so fast ... Try it again!", "Hey, wake up ... Try it again, please!", "Unfortunately wrong ... one more try!", "Are you sure? Think twice!", "Wrong ... please do it right next time!"]; translate.infoComment = "Please click on the correct answer!"; translate.resultComment = "Result"; translate.menuTitle = "Menu:"; translate.soundLabel = "sound"; translate.correctResults = "Right answers:"; translate.incorrectResults = "Wrong answers:"; translate.timeResults = "Timer:"; break; case "french": translate.calcOptions = [ "+ addition", "- soustraction", "\ xB7 multiplication", ": division"]; translate.timeString = "chronomètre (10 secondes)"; translate.positiveComment = ["Génial!", "Très bien!", "Excellent!", "Je vous ai dit, vous pouvez le faire!", "Super!"]; translate.negativeComment = ["Pas si vite ... Essayez à nouveau!", "Hey, réveille-toi ... Essayez à nouveau, s'il vous plaît!", "Malheureusement mal ... encore un essai!", "Êtes-vous sûr? Réfléchissez à deux fois!", "Mauvais ... s'il vous plaît le faire dès la prochaine fois!"]; translate.infoComment = "S'il vous plaît cliquer sur la bonne réponse!"; translate.resultComment = "Résultat"; translate.menuTitle = "Menu:"; translate.soundLabel = "sonner"; translate.correctResults = "Bonnes réponses:"; translate.incorrectResults = "Mauvaises réponses:"; translate.timeResults = "Chronomètre:"; break; case "hebrew": translate.calcOptions = ["+ בנוסף", "- חיסור", "\xB7 כפל", ": חלוק"]; translate.timeString = "שָׁעוֹן עֶצֶר"; translate.positiveComment = ["!שַׁפִּיר", "!טוב מאוד", "!מְצוּיָן", "!אמרתי לך, אתה יכול לעשות את זה", "!מושלם"]; translate.negativeComment = ["!לא כל כך מהר - לנסות את זה שוב", "!היי, תתעורר ... נסה את זה שוב, בבקשה", "!למרבה הצער לא בסדר ... עוד צ'אנס אחד", "!האם אתה בטוח? תחשוב פעמיים", "!בזמן הלא נכון ... אנא עשה זאת ממש ליד"]; translate.infoComment = "!אנא לחץ על התשובה הנכונה"; translate.resultComment = "תוֹצָאָה"; translate.menuTitle = ":תַפרִיט"; translate.soundLabel = "נשמע"; translate.correctResults = ":נכון תשובות"; translate.incorrectResults = ":תשובות לא נכונות"; translate.timeResults = ":שָׁעוֹן עֶצֶר"; break; case "spanish": translate.calcOptions = ["+ suma", "- sustracción", "\xB7 multiplicación", ": división"]; translate.timeString = "cronógrafo (10 preguntas)"; translate.positiveComment = ["Estupendo!", "Muy bien!", "Excelente!", "Te lo dije, usted puede hacerlo!", "Terrífico!"]; translate.negativeComment = ["No tan rápido ... Inténtelo de nuevo!", "Hey, despierta ... Inténtelo de nuevo, por favor!", "Por desgracia mal ... otra oportunidad!", "¿Estás seguro? ¡Pensar dos veces!", "Mal ... por favor, hacerlo bien la próxima vez!"]; translate.infoComment = "Por favor, haga clic en la respuesta correcta!"; translate.resultComment = "Resultado"; translate.menuTitle = "Menú:"; translate.soundLabel = "sonar"; translate.correctResults = "Respuestas correctas:"; translate.incorrectResults = "Respuestas incorrectas:"; translate.timeResults = "Cronógrafo:"; break; case "arabic": translate.calcOptions = ["+ خلاصة", "- طرح", "\xB7 ضرب", ": عملية القسمة"]; translate.timeString = "الكونومتر الميقات"; translate.positiveComment = ["!عظيم", "!جيد جدا", "!ممتاز", "!قلت لك، يمكنك أن تفعل ذلك", "!رائع"]; translate.negativeComment = ["!ليس بهذه السرعة ... حاول مرة أخرى", "!مهلا، أستيقظ ... حاول مرة أخرى، من فضلك", "!خاطئة للأسف ... واحدة محاولة أكثر", "!هل أنت واثق؟ فكر مرتين", "!خطأ ... يرجى القيام بذلك الوقت الحق المقبل"]; translate.infoComment = "!الرجاء النقر على الإجابة الصحيحة"; translate.resultComment = "نتيجة"; translate.menuTitle = ":قائمة طعام"; translate.soundLabel = "صوت"; translate.correctResults = ":الإجابات الصحيحة"; translate.incorrectResults = ":إجابات خاطئة"; translate.timeResults = ":الكونومتر الميقات"; break; } d3.select(".feedback").html(translate.infoComment); if (updateResult){ d3.select("div.menu").text(translate.menuTitle); var newResultComment = d3.select("div.result").html().split(":"); if (correctAnswers.length > 0 || incorrectAnswers.length > 0) { d3.select("div.result").text(translate.resultComment + ":" + (newResultComment[1] ? newResultComment[1] : "")); } showResults(); d3.selectAll("select#ro option").data(translate.calcOptions).text(function(d) {return d;}); d3.select("label.time").text(translate.timeString); d3.select("label.sound").text(translate.soundLabel); } } </script> </body> </html>
Modified
http://d3js.org/d3.v3.js
to a secure url
https://d3js.org/d3.v3.js