Created
October 23, 2015 07:39
-
-
Save edtoken/247786f9714bea41cbaa to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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