模拟google电吉他
前几天看到google为了纪念电吉他大师而作的电吉他应用。今天在论坛里看到有人讨论,就随便做了一个简化版。
1.用古典吉他(我弹得。。。)代替电吉他
2.由于是简化版,只做了一根高音的3弦。
3.google发音用的是flash,可以保证不延迟和音质。由于做flash太烦了,用html5的audio代替发音的flash。
4.google版的弦是用图片,划定区域变色,并且触发判定用的是判断rgba的方法。为了简化,我用2次曲线代替了这个过程。。。
5.弦的变色就不变了。大家会点动画的都会把。。。不过已经预留了变色接口
以下是简化版的代码。其中Class函数是写的习惯了,其实在这个应用里完全没必要
/**
*
*/
(function() {
var isIE = /msie/i.test(navigator.userAgent) && !window.opera;
function mousePosition(e) {
var event = e || window.event;
if (event.pageX || event.pageY) {
return {x:event.pageX, y:event.pageY};
}
return {
x:event.clientX + document.body.scrollLeft - document.body.clientLeft,
y:event.clientY + document.body.scrollTop - document.body.clientTop
};
}
function getPosition(target) {
var left = 0, top = 0;
do {
left += target.offsetLeft || 0;
top += target.offsetTop || 0;
target = target.offsetParent;
} while(target);
return {
left: left,
top: top
};
}
var Class = (function() {
/**
* Initialze object from class.
* @param class object.
*/
var initializeClass = (function() {
if (Object.create) {
return Object.create;
} else {
return function(o) {
function F() {}
F.prototype = o;
return new F();
};
}
})();
/**
* The main function of Class.
*
* @param classContent
* @param superClass
*/
return function() {
var classPrototype = arguments[arguments.length - 1] || {};
for (var index = 0; index < arguments.length - 1; index++) {
var superClass = arguments[index];
if (typeof superClass["initialize"] == "function") {
classPrototype.superclass = superClass["initialize"];
} else {
classPrototype.superclass = function() {};
}
for (var prop in superClass) {
if (prop == "initialize" || prop == "newInstance") {
continue;
}
if (classPrototype.hasOwnProperty(prop)) {
if (typeof superClass[prop] == "function") {
classPrototype.superclass[prop] = superClass[prop];
}
} else {
classPrototype[prop] = superClass[prop];
}
}
}
classPrototype.newInstance = function() {
var instance = initializeClass(this);
if (instance["initialize"]) {
instance["initialize"].apply(instance, arguments);
}
return instance;
};
return classPrototype;
};
})();
/**
* Canvas描绘工具类
*/
var GraphHelper = Class({
container: null,
width: null,
height: null,
ctx: null,
initialize: function(containerId, width, height) {
this.container = document.getElementById(containerId);
this.width = width;
this.height = height;
},
/**
* 初始化ctx
*/
getCtx: function() {
if (!this.ctx) {
var cvs = document.createElement('canvas');
// 这里不能用style.width,否则drawImage会有bug
cvs.width = this.width;
cvs.height = this.height;
cvs.style.borderStyle = "solid";
cvs.style.borderWidth = "1px";
cvs.style.position = 'absolute';
this.container.appendChild(cvs);
if (isIE) {
G_vmlCanvasManager.initElement(cvs);
cvs = document.getElementById(cvs.id);
}
this.canvas = cvs;
this.ctx = cvs.getContext('2d');
}
return this.ctx;
},
/**
* 清除Canvas
*/
clear: function() {
var ctx = this.getCtx();
ctx.clearRect(0, 0, this.width, this.height);
},
/**
* 注册事件
*/
addEventListener: function(event, fn) {
this.getCtx();
this.canvas.addEventListener(event, fn, false);
},
/**
* 注册Canvas Style
*/
setStyle: function(props) {
for (var prop in props) {
this.canvas.style[prop] = props[prop];
}
},
drawChord: function(x1, y1, x2, y2, cpX, cpY, strokeStyle) {
var ctx = this.getCtx();
if (!ctx) {
return null;
}
ctx.beginPath();
ctx.lineWidth = 4;
ctx.lineCap = "round";
ctx.strokeStyle = strokeStyle;
ctx.moveTo(x1, y1);
if (cpY == y2) {
ctx.lineTo(x2, y2);
} else {
ctx.quadraticCurveTo(cpX, cpY, x2, y2);
}
ctx.stroke();
}
});
var Chord = Class({
x1: null,
x2: null,
y1: null,
y2: null,
strokeStyle: null,
graphHelper: null,
audioHelp: null,
isPlay: false,
initialize: function(x1, y1, x2, y2, strokeStyle, graphHelper, audioPath) {
this.x1 = x1;
this.x2 = x2;
this.y1 = y1;
this.y2 = y2;
this.strokeStyle = strokeStyle;
this.graphHelper = graphHelper;
this.initAudio(audioPath);
var canvasInfo = getPosition(graphHelper.container);
var target = this;
graphHelper.addEventListener("mousemove", function(e) {
if (target.isPlay) {
return;
}
var pos = mousePosition(e);
var mouseX = pos.x - canvasInfo.left;
var mouseY = pos.y - canvasInfo.top;
if (mouseX > target.x1 && mouseX < target.x2 && mouseY < target.y1 + 2 && mouseY > target.y1 - 2) {
graphHelper.setStyle({
cursor: "pointer"
});
} else {
graphHelper.setStyle({
cursor: "default"
});
}
});
graphHelper.addEventListener("click", function(e) {
if (target.isPlay) {
return;
}
var pos = mousePosition(e);
var mouseX = pos.x - canvasInfo.left;
var mouseY = pos.y - canvasInfo.top;
if (mouseX > target.x1 && mouseX < target.x2 && mouseY < target.y1 + 2 && mouseY > target.y1 - 2) {
this.isPlay = true;
graphHelper.setStyle({
cursor: "default"
});
target.play();
target.audioHelper.play();
}
});
},
draw: function(cpY) {
this.graphHelper.clear();
this.graphHelper.drawChord(this.x1, this.y1, this.x2, this.y2, (this.x1 + this.x2) / 2, cpY + this.y2, this.strokeStyle);
},
initAudio: function(audioPath) {
this.audioHelper = document.createElement("audio");
this.audioHelper.src = audioPath;
document.body.appendChild(this.audioHelper);
},
play: function() {
var target = this;
var cpyList = initCpy(20, 4);
function initCpy(maxStep, step) {
var cpyList = [];
for (var index = 0; index < maxStep; index++) {
cpyList.splice(0, 0, index);
}
while (maxStep != 0) {
for (var index = maxStep; index > -(maxStep - step); index --) {
cpyList.splice(0, 0, index);
}
maxStep = -(maxStep - step);
for (var index = maxStep; index < -(maxStep + step); index++) {
cpyList.splice(0, 0, index);
}
maxStep = -(maxStep + step);
if (maxStep < 0) {
maxStep = 0;
}
}
if (cpyList[0] != 0) {
cpyList.splice(0, 0, 0);
}
return cpyList;
}
var animation = function() {
var currentIndex = cpyList.pop();
target.draw(currentIndex);
if (cpyList.length > 0) {
setTimeout(animation, 10);
} else {
target.isPlay = false;
}
};
setTimeout(animation, 10);
}
});
Guitar = function(containerId, width, height) {
var helper = GraphHelper.newInstance(containerId, width, height);
var chord = Chord.newInstance(20, 40, 240, 40, "rgba(190, 190, 190, 1)", helper, "music/3n.ogg");
chord.draw(0);
};
})();
如果大家希望在以上代码上扩展,需要注意以下问题:
1.Audio应该独立出来,另起一个类单独管理。这里为了简化。。。。
2.setTimeout应该独立出来单独管理,记得以前写过一篇文章关于封装setTimeout的。。。
3.Class的写法在这里没有必要,大家如果要扩展,并且存在继承关系的可以保留
4.setTimeout这块应封装成from, to , func, duration的形式,这样在改变动画总时间上具有可扩展性。也就是要封装出一个Tween出来。这里为了简化。。。(&昨天晚上玩到很晚,没精神写。。。。)
5,只支持Chrome10以上,Firefox 4以上
- 本文附件下载:
- Guitar.zip (38.3 KB)
作者: rainsilence
_________________________________________________________________
为了纪念电吉他之父莱斯·保罗(Les Paul,1915年6月9日-2009年8月13日)诞辰96周年,Google Doodle又出神作,首页的Logo被设计成一个电吉他形,并且鼠标滑过会弹出美妙的音乐,当你按下键盘图标时,还可以直接用键盘弹奏这个“电吉他”。
弹了几下,发现自己很有音乐天赋呢,于是决定保存下来,永久收藏。
下面是我从Google首页上抠下来的电吉他源代码,去掉了无关代码,仅供喜欢这个Doodle的朋友收藏,Google保留所有权利。
Google 电吉他 在线演示:
带录音回放功能的 Google 电吉他 在线演示:
录音以后会出现类似 “http://www.google.com/webhp?tune=EAwiYAgkBETAQmAgJAA*” 这样的直接播放URL,把“http://www.google.com/webhp” 替换成你自己的电吉他URL,同样可以自动播放。
Google 电吉他 源代码打包下载:
Google电吉他源代码
上面压缩包中已经包含了新的带录音回放功能的电吉他(recordable-guitar.htm)和老的电吉他(google-guitar.htm)。
只要使用iframe 引用 “google-guitar.htm” 或者 “recordable-guitar.htm” ,就可以把整个电吉他效果加到你的独立博客或网站中去了。
如,在你的博客中添加如下代码:
<iframe src="/uploads/guitar/google-guitar.htm" height="340px" width="660px" style="overflow:hidden" frameborder="0" ></iframe>BSP会过滤大部分HTML代码,所以此方法主要适用于独立博客。
Google 电吉他 不发声的解决方案:
当然你也可以在本地直接打开 google-guitar.htm 或 recordable-guitar.htm 来弹奏吉他,不过会由于Flash安全设置而无法发声,解决方法:
- 上传全部六个文件到服务器,然后用网址的形式打开,可以正常发声,通过IE/FF/Chrome测试。
- http://www.macromedia.com/support/documentation/cn/flashplayer/help/settings_manager04.html
打开全局安全设置面板,在这个页面上会加载本地的Flash设置,选择“编辑多个位置–添加位置”,将guitar11.swf所在文件夹加入到信任路径里,就可以本地播放了。(感谢网友Top Fun留言) - 在本地用浏览器打开guitar11.swf,右键–全局设置–高级–受信任位置设置,找到并添加guitar11.swf即可。(感谢网友罗汉果提供)
Google 电吉他 谱子:
网友们(找不到最原始出处,抱歉)提供了好多谱子呢,这里挑几首简单的,先按下键盘图标,然后用键盘弹,记得关闭输入法。
- 两只老虎 (我的最爱^_*)
- TYUT TYUT UIO UIO OPOIUT OPOIUT UWT UWT
- 沧海一声笑
- POUYT UYTEW WEWETYUOP PPOUYTY
- 隐形的翅膀
- 358787 6568321 11186532122 358787 6568321 1118653211
- 天空之城(优美)
- 6787807 365685 254573 874477 6787807 365685 34878908 876756 1232352 5878007 678789855 43213 376321 21253 376321 21276
本文地址:http://www.zhujun.org/web/google-doodle-guitar-source-code/本文链接地址: 模拟google电吉他






![雯雯超市-淘宝店[正品真维斯,CK代购] 雯雯超市-淘宝店](http://emake.info/images/taobao.png)





