Skip to content

Instantly share code, notes, and snippets.

@edtoken
Created October 23, 2015 07:39
Show Gist options
  • Save edtoken/247786f9714bea41cbaa to your computer and use it in GitHub Desktop.
Save edtoken/247786f9714bea41cbaa to your computer and use it in GitHub Desktop.
(function ($) {
var requestAnimationFrame = window.requestAnimationFrame
|| window.mozRequestAnimationFrame
|| window.webkitRequestAnimationFrame
|| window.msRequestAnimationFrame;
window.requestAnimationFrame = requestAnimationFrame;
var TO_RADIANS = Math.PI / 180;
function drawRotatedImage(context, image, x, y, angle, offset, sizeFix) {
var offset = offset || [0, 0];
var imgSize = sizeFix || [image.width, image.height];
var arg = [];
context.save();
context.translate(x, y);
context.rotate(angle * TO_RADIANS);
var imgX = -(imgSize[0] / 2) + offset[0];
var imgY = -(imgSize[1] / 2) + offset[1];
var imgW = imgSize[0];
var imgH = imgSize[1];
arg.push(image);
arg.push(imgX);
arg.push(imgY);
arg.push(imgW);
arg.push(imgH);
// убрать прозрачность
//context.globalAlpha = 0.4
//context.lineWidth = 1
//context.strokeStyle = 'blue';
//context.rect(x, y, imgW, imgH);
//context.stroke();
context.drawImage.apply(context, arg);
context.restore();
}
// поправляет координаты элемента перед отрисовкой
// в соответствии с текущим размером layout
function fixItemCoordinates(coord, data) {
var c = coord.slice(0);
c[0] = c[0] * data.c + data.x;
c[1] = c[1] * data.c + data.y;
return c;
};
// факториал
function fact(n) {
return (n <= 1) ? 1 : n * fact(n - 1);
}
// http://habrahabr.ru/post/163073/
// i - номер вершины, n - количество вершин, t - положение кривой (от 0 до 1)
function getBezierBasis(i, n, t) {
// считаем i-й элемент полинома Берштейна
return (fact(n) / (fact(i) * fact(n - i))) * Math.pow(t, i) * Math.pow(1 - t, n - i);
}
// arr - массив опорных точек. Точка - двухэлементный массив, (x = arr[0], y = arr[1], radians[2])
// step - шаг при расчете кривой (0 < step < 1), по умолчанию 0.01
function getBezierCurve(arr, step, speed) {
if (step == undefined) {
step = 0.01;
}
var res = new Array()
for (var t = 0; t < 1 + step; t += step) {
if (t > 1) {
t = 1;
}
var ind = res.length;
res[ind] = new Array(0, 0, 0, 0, 0);
for (var i = 0; i < arr.length; i++) {
var b = getBezierBasis(i, arr.length - 1, t);
res[ind][0] += arr[i][0] * b;
res[ind][1] += arr[i][1] * b;
res[ind][2] += arr[i][2] * b;
res[ind][3] += arr[i][3] * b;
res[ind][4] += arr[i][4] * b;
}
}
return res;
}
function getCurrentFlow(item, process) {
var start = false;
var end = false;
for (var n in item.flow) {
if (n > process) {
end = n;
break;
}
if (n <= process) {
start = n;
}
}
if (start === false) return false;
end = parseInt(end) || 100;
start = parseInt(start);
item.flowCalc[start] = item.flowCalc[start] || getBezierCurve(item.flow[start]);
return {
flow: item.flowCalc[start],
start: start,
end: end,
lengtn: end - start
}
}
function getNewItemSize(img, c) {
if (!img || !c) return false;
return [img.width * c, img.height * c];
}
var App = {};
App.defaults = {
backendOptions: appOptions || [],
bgBasePath: '/build/img/game2/bg-n.png',
bgBasePathDebug: '/build/img/game2/bg-n-debug.png',
//bgBasePathDebug: '/build/img/game2/bg-n-debug2.png',
bgPath: false,
elements: {
oblaka: {
item: false,
src: '/build/img/game2/oblaka.png'
},
k1: {
item: false,
src: '/build/img/game2/k1.png'
},
k2: {
item: false,
src: '/build/img/game2/k2.png'
},
kran1: {
item: false,
src: '/build/img/game2/kran1.png'
},
'g-1-1': {
item: false,
src: '/build/img/game2/g-1-1.png'
},
box1: {
item: false,
src: '/build/img/game2/box-1.png'
},
box2: {
item: false,
src: '/build/img/game2/box-2.png'
},
box3: {
item: false,
src: '/build/img/game2/box-3.png'
},
box4: {
item: false,
src: '/build/img/game2/box-4.png'
},
box5: {
item: false,
src: '/build/img/game2/box-5.png'
},
car1: {
item: false,
src: '/build/img/game2/car1.png'
},
car2: {
item: false,
src: '/build/img/game2/car2.png'
},
car3: {
item: false,
src: '/build/img/game2/car3.png'
},
car4: {
item: false,
src: '/build/img/game2/car4.png'
},
poezd1: {
item: false,
src: '/build/img/game2/poezd1.png'
},
poezd2: {
item: false,
src: '/build/img/game2/poezd2.png'
},
pog1: {
item: false,
src: '/build/img/game2/pog1.png'
},
pog2: {
item: false,
src: '/build/img/game2/pog2.png'
},
kr1: {
item: false,
src: '/build/img/game2/kr1.png'
},
kr2: {
item: false,
src: '/build/img/game2/kr2.png'
},
kr3: {
item: false,
src: '/build/img/game2/kr3.png'
},
}
};
App.data = {
debug: false,
width: false,
height: false,
top: false,
left: false,
animationTime: 25000, // время анимации
frame: 0, // текущий кадр
frames: 0, // общее количество кадров за 100% времени анимации
process: 0, // текущий процесс выполнения
time: {
start: false,
end: false,
process: false
},
background: {
origin: {
width: 0,
height: 0
},
render: {
width: 0,
height: 0,
x: 0,
y: 0
}
},
items: [
{
name: 'g-1-1',
flow: {
160: [
[506, 807]
],
310: [
[506, 807]
]
},
},
{
name: 'box1',
flow: {
180: [
[526, 843]
],
320: [
[526, 843]
]
},
},
{
name: 'box2',
flow: {
190: [
[570, 675]
],
330: [
[570, 675]
]
},
},
{
name: 'box3',
flow: {
200: [
[615, 880]
],
340: [
[615, 880]
]
},
},
{
name: 'box4',
flow: {
760: [
[1347, 199]
],
1050: [
[1347, 199]
]
},
},
{
name: 'box5',
flow: {
780: [
[1587, 225]
],
1100: [
[1587, 225]
]
},
},
{
name: 'car1',
flow: {
0: [[-100, 537, -39]],
10: [
[0, 537, -39],
[50, 517, -39],
[100, 512, -30],
[150, 479, 15],
[200, 407, 35],
[225, 637, 120],
[250, 777, -5],
[300, 697, -10],
[350, 692, -10],
[400, 697, -10],
[450, 712, -10],
[500, 717, -5],
[530, 727, 0]
],
150: [
[530, 727, 0]
],
290:[
[530, 727, 0]
]
},
},
{
name: 'car2',
flow: {
300: [
[530, 711, -35],
[500, 701, -40],
[450, 696, -50],
[400, 681, -50],
[350, 676, -40],
[300, 677, -10],
[250, 761, -0],
[225, 621, 20],
[200, 391, 20],
[150, 463, -30],
[100, 497, -60],
[50, 501, -70],
[0, 520, -70],
],
450: [
[0, 520, -70],
]
},
},
{
name: 'car3',
flow: {
600: [
[1920, 344, -22],
],
601: [
[1920, 344, -22],
[1870, 359, -15],
[1820, 364, -10],
[1720, 359, -5],
[1720, 359, -5],
[1670, 357, 5],
[1615, 302, 25]
],
750: [
[1615, 302, 25]
],
1040:[
[1615, 302, 25]
]
},
},
{
name: 'car4',
flow: {
1050: [
[1615, 295, 50],
],
1100: [
[1615, 295, 50],
[1670, 350, 27],
[1720, 352, 10],
[1770, 357, 7],
[1820, 357, 0],
[1870, 352, -7],
[1920, 337, -7]
],
1300: [
[1920, 337, -7]
]
},
},
{
name: 'k1',
flow: {
0: [
[384, 398]
],
1500: [
[384, 398]
]
},
},
{
name: 'k2',
flow: {
0: [
//[610, 730, -52],
[680, 650, -52],
],
// начал движение
375: [
[680, 650, -52],
[720, 550, -52],
[655, 450, -180]
],
562: [
[655, 450, -180],
[1180, 390, -180],
[1270, 370, -200],
[1310, 375, -204],
],
// пришел на второй берег остановился стоянка
750: [
[1310, 375, -204],
],
// стоянка закончилась начал движение обратно
1125: [
[1310, 375, -204],
[1260, 360, -200],
],
1165: [
[1260, 360, -200],
[1100, 200, -50],
[1170, 250, -40],
[970, 550, -30],
[800, 575, -30],
[700, 585, -52],
[680, 650, -52],
],
// путь обратно завершен
1500: [
[680, 650, -52],
]
},
},
{
name: 'kr1',
flow: {
0: [
[610, 703, 0]
],
160: [
[610, 703, 0],
[600, 703, 0]
],
200: [
[600, 703, 0],
[610, 703, 0],
],
240: [
[610, 703, 0],
],
1500: [
[610, 703, 0]
]
},
},
{
name: 'kr2',
flow: {
0: [
[610, 703, 0]
],
180: [
[610, 703, 0],
[600, 703, 0]
],
220: [
[600, 703, 0],
[610, 703, 0],
],
260: [
[610, 703, 0],
],
1500: [
[610, 703, 0]
]
},
},
{
name: 'kr3',
flow: {
0: [
[610, 703, 0]
],
190: [
[610, 703, 0],
[600, 703, 0]
],
230: [
[600, 703, 0],
[610, 703, 0],
],
290: [
[610, 703, 0],
],
1500: [
[610, 703, 0]
]
},
},
{
name: 'kran1',
flow: {
0: [
[1500, 302],
],
750: [
[1500, 302],
[1380, 394],
],
850: [
[1380, 394],
[1500, 302]
],
950: [
[1500, 302]
],
1500: [
[1500, 302]
]
},
},
{
name: 'poezd1',
flow: {
0: [
[322, 1068]
],
50: [
[322, 1068],
[390, 924]
],
150: [
[390, 924]
],
250: [
[390, 924]
],
280: [
[390, 924],
[322, 1068]
],
330: [
[322, 1068]
],
1500: [
[322, 1068]
]
},
},
{
name: 'poezd2',
flow: {
0:[
[1713, -22]
],
650:[
[1713, -22],
[1603, 52]
],
750:[
[1603, 52]
],
850:[
[1603, 52],
[1713, -22],
],
950:[
[1713, -22],
]
},
},
{
name: 'pog1',
flow: {
0: [
[565, 830],
],
150: [
[565, 830],
[540, 760]
],
250: [
[540, 760],
[565, 830]
],
400: [
[565, 830]
],
1500: [
[565, 830]
]
},
},
{
name: 'pog2',
flow: {
0: [
[1470, 220],
],
750: [
[1470, 220],
],
760: [
[1470, 220],
[1600, 280, 50],
],
910: [
[1600, 280, 50],
[1470, 220],
],
1060: [
[1470, 220],
],
1500: [
[1470, 220],
]
},
},
{
name: 'oblaka',
flow: {
0:[
[960, 500]
],
1500:[
[960, 500]
]
//0: [
// [0, 500]
//],
//1: [
// [0, 500],
// [480, 500],
//],
//375: [
// [480, 500],
// [960, 500],
//],
//750: [
// [960, 500],
// [1440, 500],
//],
//1125: [
// [1440, 500],
// [1920, 500],
//],
//1500: [
// [1920, 500]
//]
}
}
]
};
App.dom = {
bg: 0,
canvas: 0
};
App.ctx = false;
App.fn = {};
App.fn.getPageSize = function () {
return {
width: $(document).width(),
height: $(document).height()
}
};
App.fn.getWindowSize = function () {
return {
width: $(window).width(),
height: $(window).height()
}
};
/**
* рассчет размеров
*/
App.fn.setLayoutSize = function () {
_.extend(App.data, App.fn.getPageSize());
App.dom.node.width = App.data.width;
App.dom.node.height = App.data.height;
};
App.initialize = function () {
// считаю общее количество кадров
App.data.frames = Math.ceil(App.data.animationTime / 16.6666667);
console.log('start animation frames:' + App.data.frames);
App.dom.$mainBackgroundCanvas = $('.js-mainBackground-canvas');
App.dom.node = App.dom.$mainBackgroundCanvas[0];
App.ctx = App.dom.node.getContext('2d');
// load images
var images = {};
images.main = App.defaults.bgPath;
var Img = new Image();
Img.onload = function () {
App.dom.bg = this;
App.data.top = $(window).scrollTop();
App.data.left = $(window).scrollLeft();
App.calcDomData();
App.drawImage();
App.draw();
};
Img.onerror = function () {
};
Img.src = images.main;
var elements = App.defaults.elements;
for (var item in elements) {
(function (el, name) {
var Img = new Image();
Img.onload = function () {
App.defaults.elements[name].item = this;
};
Img.src = App.defaults['base-url'] + '' + el.src;
})(elements[item], item);
}
};
// рассчет размеров изображения
App.calcDomData = function () {
App.data.background.origin = {
width: App.dom.bg.width || App.dom.bg.naturalWidth || $(App.dom.bg).width(),
height: App.dom.bg.height || App.dom.bg.naturalWidth || $(App.dom.bg).height()
};
App.data.background.render = {
width: 0,
height: 0,
x: 0,
y: 0,
c: 0 // коэфицент изменения размера изображения
};
var d1 = App.data.width / App.data.background.origin.width;
var d2 = App.data.height / App.data.background.origin.height;
var c = (d1 > d2) ? d1 : d2;
App.data.background.render.c = c;
App.data.background.render.width = App.data.background.origin.width * c;
App.data.background.render.height = App.data.background.origin.height * c;
App.data.background.render.x = (App.data.background.render.width > App.data.width) ? -((App.data.background.render.width - App.data.width) / 2) : App.data.background.render.x;
App.data.background.render.y = (App.data.background.render.width > App.data.height) ? -((App.data.background.render.height - App.data.height) / 2) : App.data.background.render.y;
};
// рассчет данных для отрисовки
App.calcBaseData = function () {
var data = App.data;
var base = {
debug: data.debug,
el: App.defaults.elements,
process: App.data.process,
frame: App.data.frame,
x: false,
y: false,
centerX: false,
centerY: false,
background: data.background
};
// теперь нужно посчитать общее количество кадров
//var count = data.animationTime / 1000 * 60;
//console.log('t', timeSeconds)
base.c = data.background.render.c; // коэфицент изменения размера изображения
base.x = data.background.render.x; // startX (смещение)
base.y = data.background.render.y; // startY (смещение)
base.w = data.background.render.width; // ширина
base.h = data.background.render.height; // высота
base.centerX = base.background.origin.width / 2 * base.c;
base.centerY = (base.background.origin.height / 2 * base.c) + base.y;
base.items = data.items;
return base;
};
// отрисовка изображения
App.drawImage = function () {
if (!App.ctx) return false;
// пересчитываю размеры background
App.calcDomData();
};
// main draw method
App.draw = function () {
requestAnimationFrame(App.draw);
if (!App.ctx) return false;
if (App.data.debug) {
console.log('draw', App.data.frame, '[', App.data.frames, ']', 'ready:' + App.data.process + '%');
}
var ctx = App.ctx;
var data = App.calcBaseData();
ctx.font = "9px Arial";
ctx.clearRect(0, 0, data.w, data.h);
App.drawImage();
ctx.drawImage(
App.dom.bg,
data.background.render.x,
data.background.render.y,
data.background.render.width,
data.background.render.height
);
// animation
for (var i in data.items) {
if (data.items[i].ready == undefined) {
data.items[i].ready = true;
}
if (data.items[i].type == undefined) {
data.items[i].type = 'base';
}
if (data.items[i].flowCalc == undefined) {
data.items[i].flowCalc = {};
}
if (!data.items[i].ready) continue;
var item = data.items[i];
var flowData = getCurrentFlow(item, data.frame);
if (!flowData) continue;
// считаю процент выполенния текущего подшага
var pr = data.frame - flowData.start;
var end = flowData.end - flowData.start;
var stepProcess = Math.ceil(pr / end * 100);
var flow = flowData.flow[stepProcess];
if (!flow) continue;
var coord = fixItemCoordinates(flow, data);
var posFix = [0, 0];
// поправляю координаты
if (item.positionFix) {
for (var i in item.positionFix) {
posFix[i] += item.positionFix[i];
}
}
if (data.el[item.name]) {
if (data.el[item.name].item) {
var sizeFix = getNewItemSize(data.el[item.name].item, data.c);
switch (item.type) {
case 'cropImage':
drawRotatedImage(
ctx,
data.el[item.name].item,
coord[0],
coord[1],
coord[2],
posFix,
sizeFix
);
break;
default:
drawRotatedImage(
ctx,
data.el[item.name].item,
coord[0],
coord[1],
coord[2],
posFix,
sizeFix
);
break;
}
}
} else {
ctx.beginPath();
ctx.arc(coord[0], coord[1], 3, 0, 2 * Math.PI, false);
ctx.fillStyle = 'red';
ctx.fill();
}
}
// calc next step
App.data.frame++;
if (App.data.frame > App.data.frames) {
App.data.frame = 0;
}
App.data.process = App.data.frame / App.data.frames * 100;
App.data.processCeil = Math.ceil(App.data.process);
};
$(document).ready(function () {
for (var i in App.defaults.backendOptions) {
var option = App.defaults.backendOptions[i];
if (!option.type) option.type = 'options';
if (!option.name || !option.value) continue;
var optionObj = {};
optionObj[option.name] = option.value;
if (!App.defaults[option.type]) {
App.defaults[option.type] = {};
}
_.extend(App.defaults[option.type], optionObj);
}
App.defaults['base-url'] = App.defaults.options['base-url'];
App.defaults.bgPath = App.defaults['base-url'];
App.defaults.bgPath += (App.data.debug)
? App.defaults.bgBasePathDebug
: App.defaults.bgBasePath;
App.initialize();
App.fn.setLayoutSize();
console.log('App.game', App);
});
$(document).on('scroll', function () {
App.data.top = $(window).scrollTop();
App.data.left = $(window).scrollLeft();
});
$(window).on('orientationchange resize', function (event) {
App.fn.setLayoutSize();
});
})(jQuery);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment