« »

模拟google电吉他

2011年6月12日 | 标签: , ,

前几天看到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以上

 

作者: rainsilence

_________________________________________________________________

为了纪念电吉他之父莱斯·保罗(Les Paul,1915年6月9日-2009年8月13日)诞辰96周年, Doodle又出神作,首页的Logo被设计成一个电吉他形,并且鼠标滑过会弹出美妙的音乐,当你按下键盘图标时,还可以直接用键盘弹奏这个“”。

弹了几下,发现自己很有音乐天赋呢,于是决定保存下来,永久收藏。

下面是我从Google首页上抠下来的电吉他源代码,去掉了无关代码,仅供喜欢这个Doodle的朋友收藏,Google保留所有权利。

Google 电吉他 在线演示:

 

Google 6月10日再度更新,在www.google.com推出了带录音回放功能电吉他,按下原来的键盘图标就可以,弹完以后再按一次,点播放键。

带录音回放功能的 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安全设置而无法发声,解决方法:

  1. 上传全部六个文件到服务器,然后用网址的形式打开,可以正常发声,通过IE/FF/Chrome测试。
  2. http://www.macromedia.com/support/documentation/cn/flashplayer/help/settings_manager04.html
    打开全局安全设置面板,在这个页面上会加载本地的Flash设置,选择“编辑多个位置–添加位置”,将guitar11.swf所在文件夹加入到信任路径里,就可以本地播放了。(感谢网友Top Fun留言)
  3. 在本地用浏览器打开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电吉他

无觅相关文章插件,快速提升流量


目前还没有任何评论.

使用腾讯微博登陆

使用新浪微博登陆