现在在做一个有关图片处理的项目,用到了一些HTML5中的新特性,但是在颜色方面没有找到一个比较好用的取色器,所以就打算自己写一个。
实现过程比较简单,由于是第一次写类似插件的JS,涉及到了闭包问题,对于我来说还是第一次接触,所以难免遇到一些问题。
问题描述:
我 在$.fn.colorpicker = function (callbackFun, colorType)中接收两个参数(回调函数和返回值类型),问题是在我在两个地方调用$.fn.colorpicker并传递不同回调函数参数时,无 法更改包中的callbackFun值,导致每次返回的颜色都传递到第一次调用的回调函数中,希望有大牛帮我解释一下。
以下是js全部代码(暂时只支持火狐浏览器,其它未作兼容,实现比较匆忙,有什么需要改进的地方欢迎指点)
/*
colorpicker 1.0
Copyright (c) 2013 Jia Wencheng
Email:jiawencheng06@163.com
Name:colorpicker
Description:This a jQuery (Interface) colorpicker.
*/
(function ($) {
$.fn.colorpicker = function (callbackFun, colorType) {
originObj = $(this);
colorType = colorType;
callbackFun = callbackFun;
$(document).ready(function () {
if (!$("#divMain")[0]) {
createElement();
initColorMapBar();
initColorMap([0, 0, 0]);
}
});
if ($("#divMain").css("display") == "none") {
$("#divMain").css("display", "block");
initColorPicker();
}
else {
$("#divMain").css("display", "none");
}
function initColorPicker() {
var offsetTop;
if ((originObj.offset().top + originObj.height() / 2) < 179) {
offsetTop = 0;
}
else if ((originObj.offset().top + originObj.height() / 2 + 179) > window.innerHeight) {
offsetTop = window.innerHeight - 356;
}
else {
offsetTop = originObj.offset().top + originObj.height() / 2 - 179;
}
var offsetLeft;
if ((window.innerWidth - originObj.offset().left - originObj.width()) < 325) {
offsetLeft = originObj.offset().left - 325;
}
else {
offsetLeft = originObj.offset().left + originObj.width() + 10;
}
$(divMain).css("top", offsetTop);
$(divMain).css("left", offsetLeft);
}
function createElement() {
divMain = document.createElement("div");
divMain.id = "divMain";
$(divMain).css("width", 315).css("height", 356).css("background-color", "#E8E8E8").css("position", "absolute").css("border", "1px solid #CCCCCC").css("-moz-user-select", "none").css("box-shadow", "10px 10px 10px rgba(0, 0, 0, 0.2)").css("display", "none").css("z-index", "10000");
divHeader = document.createElement("div");
divHeader.id = "divHeader";
$(divHeader).css("height", 28).css("background", "-moz-linear-gradient(center top , #FFFFFF, #E5E5E5) repeat scroll 0 0 transparent").css("position", "relative").css("font-weight", 300).css("color", "#212121").css("cursor", "move").css("border-bottom", "1px solid #CCCCCC").css("box-shadow", "0 1px 0 0 #F4F4F4");
divHeader.onmousedown = divHeaderMouseDown;
divHeader.onmouseup = divHeaderMouseUp;
divHeader.onmousemove = divHeaderMouseMove;
divHeader.onmouseout = divHeaderMouseOut;
spanName = document.createElement("span");
spanName.id = "spanName";
$(spanName).css("height", 28).css("padding-left", 20).css("line-height", "28px").css("text-shadow", "0 1px 0 #FFFFFF").css("background-color", "transparent").css("vertical-align", "top").css("word-wrap", "break-word").css("font-weight", 600).css("font-size", "12px").css("font-family", "HelveticaNeue-Light,Helvetica Neue Light,Helvetica Neue,Helvetica,Arial,Lucida Grande,sans-serif").css("float", "left").html("Color");
spanName.click = cpClose;
divHeader.appendChild(spanName);
spanClose = document.createElement("span");
spanClose.id = "spanClose";
$(spanClose).css("background", "url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAArUlEQVQoz2OYOXMmAxSzAXEOEB8A4p9QfAAqxgZTB1MsDcTngfg/DnweqoYBZvIlPIphGKSGjQFqJUxwPxA7QE1cj8SGyeeANBxGMwmkyACIBYC4AE3uMAPUc8iC9////w8SV8DirJ/YNPSDNEA17UeT+wwSPIZsOtQpDUAcALXlPZL8MZCGfCSB9VDF/6EKG9A8nU9WsMIi7hIBxdLIMQ2zKR+aHODBCBWDJw0AyNRnE3i8MH4AAAAASUVORK5CYII=') no-repeat scroll 0 0 transparent").css("cursor", "pointer").css("height", "12px").css("width", "12px").css("opacity", "0.7").css("overflow", "hidden").css("position", "absolute").css("right", "15px").css("top", "7px").attr("title", "Close");
spanClose.onclick = cpClose;
divHeader.appendChild(spanClose);
$(spanClose).mouseover(function () {
$(this).css("opacity", "1");
});
$(spanClose).mouseout(function () {
$(this).css("opacity", "0.7");
});
divMain.appendChild(divHeader);
divCanvas = document.createElement("div");
divCanvas.id = "divCanvas";
$(divCanvas).css("height", 280).css("position", "relative").css("box-shadow", "0 1px 0 0 #F4F4F4").css("border-bottom", "1px solid #CCCCCC");
divCPSubmit = document.createElement("div");
divCPSubmit.id = "divCPSubmit";
$(divCPSubmit).css("height", 40);
canvasMap = document.createElement("canvas");
canvasMap.id = "canvasMap";
contextMap = canvasMap.getContext("2d");
canvasMap.setAttribute("height", 260);
canvasMap.setAttribute("width", 260)
$(canvasMap).css("float", "left").css("position", "absolute").css("top", "10px").css("left", "10px");
divCanvas.appendChild(canvasMap);
canvasMapCover = document.createElement("canvas");
canvasMapCover.id = "canvasMapCover";
contextMapCover = canvasMapCover.getContext("2d");
canvasMapCover.setAttribute("height", 260);
canvasMapCover.setAttribute("width", 260)
$(canvasMapCover).css("float", "left").css("position", "absolute").css("top", "10px").css("left", "10px").css("cursor", "crosshair").css("background-color", "transparent");
canvasMapCover.onclick = colorMapClicked;
divCanvas.appendChild(canvasMapCover);
canvasMapBar = document.createElement("canvas");
canvasMapBar.id = "canvasMapBar";
contextMapBar = canvasMapBar.getContext("2d");
canvasMapBar.setAttribute("height", 260);
canvasMapBar.setAttribute("width", 30)
$(canvasMapBar).css("float", "left").css("position", "absolute").css("top", "10px").css("right", "10px");
divCanvas.appendChild(canvasMapBar);
canvasMapBarCover = document.createElement("canvas");
canvasMapBarCover.id = "canvasMapBarCover";
contextMapBarCover = canvasMapBarCover.getContext("2d");
canvasMapBarCover.setAttribute("height", 260);
canvasMapBarCover.setAttribute("width", 30)
$(canvasMapBarCover).css("float", "left").css("position", "absolute");
$(canvasMapBarCover).css("position", "absolute").css("top", "10px").css("right", "10px").css("cursor", "crosshair");
canvasMapBarCover.onclick = colorMapBarClicked;
divCanvas.appendChild(canvasMapBarCover);
divMain.appendChild(divCanvas);
divFooter = document.createElement("div");
divFooter.id = "divFooter";
$(divFooter).css("height", "26px").css("float", "left").css("padding", "10px");
divApply = document.createElement("div");
divApply.id = "divApply";
$(divApply).css("background", "none repeat scroll 0 0 transparent").css("border", "1px solid #CCCCCC").css("font-size", "100%").css("margin", "0px").css("outline", "0 none").css("padding", "0px").css("border-radius", "4px").css("vertical-align", "baseline").css("height", "26px").css("float", "left");
divColor = document.createElement("div");
divColor.id = "divColor";
$(divColor).css("float", "left").css("background-color", "#DB0268").css("height", "13px").css("padding", "6px 5px 7px").css("width", "10px").css("border-radius", "4px 0 0 4px");
divApply.appendChild(divColor);
txtColorValue = document.createElement("input");
txtColorValue.id = "txtColorValue";
txtColorValue.type = "text";
$(txtColorValue).css("float", "left").css("box-shadow", "0 -1px 0 0 rgba(255, 255, 255, 0.5) inset, 0 1px 0 rgba(255, 255, 255, 0.5)").css("color", "#444444").css("font-size", "11px").css("padding", "6px 5px 7px").css("width", "50px").css("text-transform", "uppercase").css("text-align", "center").css("border", "0px").css("-moz-user-select", "text");
txtColorValue.onkeyup = txtColorValueKeyUp;
divApply.appendChild(txtColorValue);
btnApply = document.createElement("input");
btnApply.id = "btnApply";
btnApply.type = "button";
$(btnApply).css("background-color", "#DB0268").css("box-shadow", "0 1px 0 rgba(255, 255, 255, 0.3) inset, 0 1px 0 rgba(255, 255, 255, 0.5))").css("border", "0px").css("border-radius", "0px 4px 4px 0px").css("color", "#FFFFFF").css("cursor", "pointer").css("float", "left").css("font-size", "11px").css("padding", "5px 10px 6px 10px").css("text-shadow", "0 -1px 0 rgba(0, 0, 0, 0.5)").attr("value", "Apply");
btnApply.onclick = cpApply;
divApply.appendChild(btnApply);
divFooter.appendChild(divApply);
btnCancel = document.createElement("input");
btnCancel.id = "btnCancel";
btnCancel.type = "button";
$(btnCancel).css("background-color", "#E5E5E5").css("background-image", "-moz-linear-gradient(center top , #F4F4F4, #E5E5E5)").css("background-repeat", "repeat-x").css("box-shadow", "0 1px 0 rgba(255, 255, 255, 0.3) inset, 0 1px 0 rgba(255, 255, 255, 0.5)").css("border", "1px solid #D1D1D1").css("border-radius", "4px 4px 4px 4px").css("color", "#212121").css("text-shadow", "0 1px 1px rgba(255, 255, 255, 0.75)").css("cursor", "pointer").css("float", "right").css("position", "relative").css("left", "90px").css("margin-bottom", "0px").css("font-size", "11px").css("-moz-user-select", "-moz-none").css("padding", "5px 10px 6px 10px").attr("value", "Cancel");
btnCancel.onclick = cpClose;
divApply.appendChild(btnCancel);
divFooter.appendChild(btnCancel);
divMain.appendChild(divFooter);
jQuery("body").prepend(divMain);
$("#divCPMain").css("display", "block");
};
function initColorMapBar() {
var color = 0;
var fillColor = contextMapBar.createLinearGradient(1, 1, 30, 1);
for (var i = 0; i < 6; i++) {
color = i % 2 == 0 ? 0 : 255;
for (var j = 1; j < 45; j++) {
if (i % 2 == 0) {
if (color > 248 && color <= 255) {
color = 255;
}
else {
color = color + 6;
}
}
else {
if (color < 7 && color >= 0) {
color = 0;
}
else {
color = color - 6;
}
}
switch (i) {
case 0:
fillColor.addColorStop(0, "rgba(255,0," + color + ",1)");
break;
case 1:
fillColor.addColorStop(0, "rgba(" + color + ",0,255,1)");
break;
case 2:
fillColor.addColorStop(0, "rgba(0," + color + ",255,1)");
break;
case 3:
fillColor.addColorStop(0, "rgba(0,255," + color + ",1)");
break;
case 4:
fillColor.addColorStop(0, "rgba(" + color + ",255,0,1)");
break;
case 5:
fillColor.addColorStop(0, "rgba(255," + color + ",0,1)");
break;
}
fillColor.addColorStop(1, "rgba(0,0,0,1)");
contextMapBar.beginPath();
contextMapBar.moveTo(1, j + 43 * i);
contextMapBar.lineTo(30, j + 43 * i);
contextMapBar.lineWidth = 2;
contextMapBar.strokeStyle = fillColor;
contextMapBar.stroke();
}
}
}
function initColorMap(imageData) {
var fillColor = contextMapBar.createLinearGradient(1, 1, 1, 260);
fillColor.addColorStop(1, "rgba(0,0,0,1)");
for (var i = 0; i < 260; i++) {
fillColor.addColorStop(0, "rgba(" + (255 - Math.round((255 - imageData[0]) * i / 260)) + "," + (255 - Math.round((255 - imageData[1]) * i / 260)) + "," + (255 - Math.round((255 - imageData[2]) * i / 260)) + ",1)");
contextMap.beginPath();
contextMap.moveTo(i + 1, 1);
contextMap.lineTo(i + 1, 260);
contextMap.lineWidth = 2;
contextMap.strokeStyle = fillColor;
contextMap.stroke();
}
}
function drawMapCrossMark(coorX, coorY, contextObj) {
contextObj.clearRect(0, 0, 260, 260);
contextObj.beginPath();
contextObj.moveTo(coorX - 4, coorY);
contextObj.lineTo(coorX - 1, coorY);
contextObj.moveTo(coorX, coorY - 4);
contextObj.lineTo(coorX, coorY - 1);
contextObj.moveTo(coorX + 1, coorY);
contextObj.lineTo(coorX + 4, coorY);
contextObj.moveTo(coorX, coorY + 1);
contextObj.lineTo(coorX, coorY + 4);
contextObj.lineWidth = 1;
contextObj.strokeStyle = "#FFF";
contextObj.stroke();
}
function colorMapClicked(e) {
drawMapCrossMark(e.layerX, e.layerY, contextMapCover);
var colorData = contextMap.getImageData(e.layerX, e.layerY, 1, 1).data;
$(txtColorValue).val((colorData[0].toString(16).length == 2 ? colorData[0].toString(16) : "0" + colorData[0].toString(16)) + (colorData[1].toString(16).length == 2 ? colorData[1].toString(16) : "0" + colorData[1].toString(16)) + (colorData[2].toString(16).length == 2 ? colorData[2].toString(16) : "0" + colorData[2].toString(16)));
$(divColor).css("background-color", "#" + $(txtColorValue).val());
}
function colorMapBarClicked(e) {
drawMapCrossMark(e.layerX, e.layerY, contextMapBarCover);
var colorData = contextMapBar.getImageData(e.layerX, e.layerY, 1, 1).data;
initColorMap(colorData);
$(txtColorValue).val((colorData[0].toString(16).length == 2 ? colorData[0].toString(16) : "0" + colorData[0].toString(16)) + (colorData[1].toString(16).length == 2 ? colorData[1].toString(16) : "0" + colorData[1].toString(16)) + (colorData[2].toString(16).length == 2 ? colorData[2].toString(16) : "0" + colorData[2].toString(16)));
$(divColor).css("background-color", "#" + $(txtColorValue).val());
}
function txtColorValueKeyUp(e) {
var colorData = $(this).val().split('');
if (colorData.length >= 3 && colorData.length < 6) {
colorData = [colorData[0], colorData[0], colorData[1], colorData[1], colorData[2], colorData[2]];
}
var isColorDataIsValid = true;
for (var i = 0; i < 6; i++) {
if (!(colorData[i].charCodeAt() >= 48 && colorData[i].charCodeAt() <= 57) || (colorData[i].charCodeAt() >= 65 && colorData[i].charCodeAt() <= 70) || (colorData[i].charCodeAt() >= 97 && colorData[i].charCodeAt() <= 102)) {
isColorDataIsValid = false;
break;
}
}
if (isColorDataIsValid) {
$(divColor).css("background-color", "#" + $(txtColorValue).val());
}
}
function divHeaderMouseDown(e) {
isMouseDown = true;
layerXTemp = e.layerX;
layerYTemp = e.layerY;
$("#divMain").css("opacity", "0.9");
}
function divHeaderMouseUp(e) {
isMouseDown = false;
$("#divMain").css("opacity", "1");
}
function divHeaderMouseMove(e) {
if (isMouseDown) {
$("#divMain").css("left", e.clientX - layerXTemp);
$("#divMain").css("top", e.clientY - layerYTemp);
}
}
function divHeaderMouseOut(e) {
isMouseDown = false;
$("#divMain").css("opacity", "1");
}
function cpApply() {
if (typeof callbackFun == "function") {
if (colorType == "rgb") {
callbackFun(new colorRgb($("#divColor").css("background-color").split(')')[0].substring(4).split(',')));
}
else {
callbackFun(colorHex($("#divColor").css("background-color").split(')')[0].substring(4).split(',')).toLocaleUpperCase());
}
}
$("#divMain").css("display", "none");
}
function cpClose() {
$("#divMain").css("display", "none");
}
function colorRgb(rgb) {
this.r = rgb[0],
this.g = rgb[1],
this.b = rgb[2]
}
function colorHex(rgb) {
return "#" + colorSingelHex(parseInt(rgb[0])) + colorSingelHex(parseInt(rgb[1])) + colorSingelHex(parseInt(rgb[2]));
}
function colorSingelHex(value) {
return value.toString(16).length > 1 ? value.toString(16) : "0" + value.toString(16);
}
}
})(jQuery);
原因:由于闭包后参数和变量不会被垃圾回收机制回收,会长期贮存在内存中。
解决方案:将代码改为在闭包体外申请变量,在包内赋值——问题解决。