Skip to content

Instantly share code, notes, and snippets.

@rfprod
Last active April 22, 2017 15:48
Show Gist options
  • Save rfprod/1a157737b8cbed2b4326e9a79ec46f16 to your computer and use it in GitHub Desktop.
Save rfprod/1a157737b8cbed2b4326e9a79ec46f16 to your computer and use it in GitHub Desktop.
Learn single-digit numbers addition
<html >
<body ng-app="learningApp">
<a id="gist" class="btn" href="#" target=_blank><span class="glyphicon glyphicon-download-alt" ></span> GIST</a>
<div ng-controller="exercise1Ctrl" class="wide" load-data>
<h1 class="container">{{title}}</h1>
<div id="task-details" class="col-xs-12 col-sm-12 col-md-12 col-lg-12"></div>
<span us-spinner spinner-key="spinner-1"></span>
</div>
</body>
</html>

Learn single-digit numbers addition

Angular app for learning single-digit numbers addition. Numbers for the exercise are randomly generated. First is in range [6,9], numbers sum is in range [11,14]. User can see a scale, which visualizes the task. User can input digits. If first number is correct, its input field is blocked, second input is shown to user. If second number is correct, its input field is blocked, result input is unlocked. If result is correct, user is congratulated and offered to try again. Upon wrong number input its color turns red, and correct number is highlighted in equation on the top of the page.

A Pen by V on CodePen.

License.

var app = angular.module('learningApp',['angularSpinner']);
app.controller('exercise1Ctrl', ($scope, $window, usSpinnerService) => {
$scope.title = 'Learn single-digit numbers addition';
$scope.scale = 20;
$scope.a = Math.floor(Math.random()*(9-6+1) + 6);
$scope.b = Math.floor(Math.random()*((14-$scope.a)-(11-$scope.a)+1) + (11-$scope.a));
$scope.xCoord = [];
$scope.yModifier = 100;
$scope.textCoord = [];
$scope.yCoordRegular = [20,30];
$scope.yCoordBold = [15,35];
$scope.arc1InputCoord = 0;
$scope.arc2InputCoord = 0;
$scope.svgDefArrow = '<defs><marker id="triangle" viewBox="0 0 10 10" refX="1" refY="5" markerWidth="4" markerHeight="6" orient="auto"><path d="M2,2 L2,11 L10,6 L2,2" style="fill: red;" /></marker></defs>';
$scope.refreshPage = () => {
window.location.reload(false);
};
$scope.resized = false;
$scope.windowResized = () => {
$scope.$apply(() => {$scope.resized = true;});
usSpinnerService.spin('spinner-1');
setTimeout(() => {$scope.initResize();},750);
};
$scope.initResize = () => {
$scope.$apply(() => {
$scope.resized = false;
});
//usSpinnerService.stop('spinner-1');
$scope.refreshPage();
};
});
app.directive("loadData", ($compile) => {
return (scope, el, attrs) => {
$('#task-details').html($compile('<div id="equation" class="well well-sm"><span id="dialog"></span><h4><span id="first-item">'+scope.a+'</span> + <span id="second-item">'+scope.b+'</span> = <input type="text" id="result-input" class="form-control" placeholder="?" readonly check-result/></h4></div>')(scope));
var rulerMarks = '', rulerText = '';
for (var i=0; i<=scope.scale; i++){
scope.xCoord.push(10+(i*$('#task-details').width()/21));
if (i==0 || i==5 || i==10 || i==15 || i==20) rulerMarks += '<line class="ruler-mark" x1="'+scope.xCoord[i]+'" y1="'+(scope.yCoordBold[0]+scope.yModifier)+'" x2="'+scope.xCoord[i]+'" y2="'+(scope.yCoordBold[1]+scope.yModifier)+'" />';
else rulerMarks += '<line class="ruler-mark" x1="'+scope.xCoord[i]+'" y1="'+(scope.yCoordRegular[0]+scope.yModifier)+'" x2="'+scope.xCoord[i]+'" y2="'+(scope.yCoordRegular[1]+scope.yModifier)+'" />';
if (i<10) scope.textCoord.push(scope.xCoord[i]-3);
else scope.textCoord.push(scope.xCoord[i]-7);
if (i==0 || i==5 || i==10 || i==15 || i==20) rulerText += '<text x="'+scope.textCoord[i]+'" y="'+(47+scope.yModifier)+'" fill="black" class="bold-text">'+i+'</text>';
else rulerText += '<text x="'+scope.textCoord[i]+'" y="'+(47+scope.yModifier)+'" fill="black" class="common-text">'+i+'</text>';
}
// arc 1
var arc1 = '<path id="arc1" d="M 10,'+(10+scope.yModifier)+' q '+((scope.textCoord[scope.a]-7)/2)+' -150 '+(scope.textCoord[scope.a]-12.5)+' 0" stroke="blue" stroke-width="3" fill="none" marker-end="url(#triangle)" />';
scope.arc1InputCoord = scope.textCoord[Math.floor(scope.a/2)]-7;
if (scope.a%2 == 0) scope.arc1InputCoord = scope.arc1InputCoord-13;
else if ($(window).width() > 1000) scope.arc1InputCoord = scope.arc1InputCoord+($(window).width()/77);
else if ($(window).width() < 1000) scope.arc1InputCoord = scope.arc1InputCoord+($(window).width()/130);
var arc1Input = '<foreignObject x="'+scope.arc1InputCoord+'" y="0" width="50" height="50"><input class="form-control arc-input" id="arc1-input" type="text" check-first-value/></foreignObject>';
// arc 2
var arc2 = '<path id="arc2" d="M '+(scope.textCoord[scope.a]+2)+','+(10+scope.yModifier)+' q '+((scope.textCoord[scope.b])/2)+' -150 '+(scope.textCoord[scope.b]-9.5)+' 0" stroke="transparent" stroke-width="3" fill="none" />';
scope.arc2InputCoord = scope.textCoord[Math.floor(scope.b/2)]-15+scope.textCoord[scope.a];
if (scope.b%2 == 0) scope.arc2InputCoord = scope.arc2InputCoord-18;
else if ($(window).width() > 1000) scope.arc2InputCoord = scope.arc2InputCoord+($(window).width()/77);
else if ($(window).width() < 1000) scope.arc2InputCoord = scope.arc2InputCoord+($(window).width()/130);
var arc2Input = '<foreignObject x="'+scope.arc2InputCoord+'" y="0" width="50" height="50"><input class="form-control arc-input" id="arc2-input" type="text" check-second-value/></foreignObject>';
// svg output
$('#task-details').append($compile('<svg id="scale">'+scope.svgDefArrow+'<rect width="100%" y="'+scope.yModifier+'" height="50" class="ruler"/><line class="axis" x1="0" y1="'+(25+scope.yModifier)+'" x2="100%" y2="'+(25+scope.yModifier)+'"/>'+rulerMarks+rulerText+arc1Input+arc1+arc2Input+arc2+'</svg>')(scope));
$('#arc1-input').focus();
scope.resized = false;
$(window).on('resize',() => {
scope.windowResized();
});
}
});
app.directive('checkFirstValue', ($compile) => {
return (scope, el, attrs) => {
el.bind('input', () => {
if (scope.a == el.val()) {
el.css('color','green');
el.attr('readonly','readonly');
$('#arc2').css('stroke','blue');
$('#arc2').css("marker-end","url(#triangle)");
$('#arc2-input').css('display','block');
$('#arc2-input').focus();
if ($('#first-item').hasClass('highlighted')) $('#first-item').removeClass('highlighted');
}else{
el.css('color','rgba(255,0,0,1)');
$('#first-item').addClass('highlighted');
}
});
}
});
app.directive('checkSecondValue', ($compile) => {
return (scope, el, attrs) => {
el.bind('input',() => {
if (scope.b == el.val()) {
el.css('color','green');
el.attr('readonly','readonly');
$('#result-input').removeAttr('readonly');
$('#result-input').attr('placeholder','');
$('#result-input').focus();
if ($('#second-item').hasClass('highlighted')) $('#second-item').removeClass('highlighted');
}else {
el.css('color','rgba(255,0,0,1)');
$('#second-item').addClass('highlighted');
}
});
}
});
app.directive('checkResult',($compile) => {
return (scope, el, attrs) => {
el.bind('input', () => {
if ((scope.a + scope.b) == el.val()) {
el.css('color','green');
el.attr('readonly','readonly');
el.blur();
$('#dialog').html($compile('<span class="glyphicon glyphicon-thumbs-up"></span> Congratulations! Your answer is correct. <button type="button" class="btn btn-info" ng-click="refreshPage()">Try Again</button>')(scope));
}else el.css('color','rgba(255,0,0,1)');
});
}
});
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.2/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/spin.js/2.3.2/spin.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-spinner/0.8.1/angular-spinner.min.js"></script>
body
#gist
position: absolute
top: 0
right: 0
h1
text-align: center
.wide
width: 100%
a:hover
text-decoration: none
#task-details
#equation
text-align: center
#dialog
font-weight: bold
#result-input
display: inline-block
width: 50px
text-align: center
font-weight: bold
.highlighted
background-color: rgba(240,183,0,0.8)
#scale
width: 100%
height: 50vh
.ruler
fill: rgba(240,230,140,0.8)
.axis
stroke: rgba(254,0,0,1)
stroke-width: 1
.ruler-mark
stroke: rgba(254,0,0,1)
stroke-width: 1
.bold-text
font-weight: bold
.common-text
font-size: 0.85em
.arc-input
width: 100%
text-align: center
font-weight: bold
#arc2-input
display: none
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" />
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment