在JavaScript中读取非UTF-8编码的CSV文件时,需要使用合适的解码方式来确保正确读取和处理文件内容。

一种常见的解决方案是使用第三方库如iconv-lite来进行字符编码的转换。以下是一个使用iconv-lite库读取非UTF-8编码的CSV文件的示例代码:

首先,确保已经安装了iconv-lite库(可以通过npm进行安装)。

npm install iconv-lite

然后,在你的JavaScript代码中引入并使用iconv-lite库来读取非UTF-8编码的CSV文件:

const fs = require('fs');
const iconv = require('iconv-lite');

// 读取二进制数据
const fileBuffer = fs.readFileSync('path/to/your/csv/file.csv');

// 将非UTF-8编码的二进制数据转换为UTF-8编码的字符串
const utf8String = iconv.decode(fileBuffer, 'GBK'); // 这里假设文件采用GBK编码

// 处理UTF-8编码的字符串,例如解析CSV内容
// ...

在上述示例中,我们首先使用fs.readFileSync()方法读取CSV文件的二进制数据,得到一个Buffer对象。然后,我们使用iconv.decode()方法将非UTF-8编码的二进制数据转换为UTF-8编码的字符串,其中参数'GBK'表示源文件采用的编码方式是GBK。最后,你可以在处理UTF-8编码的字符串时,进行相应的操作,例如解析CSV内容。

请注意根据实际情况修改代码中的文件路径和编码方式,确保与你要读取的CSV文件匹配。

HTML 代码:

<!DOCTYPE html>
<html>
<head>
 <title>JavaScript 画板</title>
 <style>
  canvas {
   border: 1px solid black;
  }
 </style>
</head>
<body>
 <div>
  <canvas id="myCanvas" width="500" height="500"></canvas>
 </div>
 <div>
  <button onclick="setColor('red')">红色</button>
  <button onclick="setColor('green')">绿色</button>
  <button onclick="setColor('blue')">蓝色</button>
  <button onclick="setColor('black')">黑色</button>
  <button onclick="clearCanvas()">清除画布</button>
 </div>
 <script src="paint.js"></script>
</body>
</html>

在 HTML 中,我们创建了一个 canvas 元素和一些按钮用于设置画笔颜色和清空画布。canvas 元素用于绘制图像。

JavaScript 代码(保存在 paint.js 文件中):

var canvas = document.getElementById("myCanvas");
var context = canvas.getContext("2d");
var color = "black";
var painting = false;

function setColor(newColor) {
 color = newColor;
}

function startPainting() {
 painting = true;
}

function stopPainting() {
 painting = false;
}

function draw(event) {
 if (!painting) {
  return;
 }

 context.beginPath();
 context.arc(event.clientX - canvas.offsetLeft, event.clientY - canvas.offsetTop, 10, 0, Math.PI * 2);
 context.fillStyle = color;
 context.fill();
 context.closePath();
}

function clearCanvas() {
 context.clearRect(0, 0, canvas.width, canvas.height);
}

canvas.addEventListener("mousedown", startPainting);
canvas.addEventListener("mouseup", stopPainting);
canvas.addEventListener("mousemove", draw);

在 JavaScript 中,我们获取了 canvas 元素和用于绘制的上下文。我们还定义了一些变量,用于存储当前画笔颜色以及当前是否正在绘制。

setColor 函数用于设置画笔颜色,它会在点击颜色按钮时调用。startPainting 和 stopPainting 函数用于开启和关闭绘制,它们会在鼠标按下和松开时调用。draw 函数用于实际的绘制操作,它会在鼠标移动时调用。clearCanvas 函数用于清空画布,它会在点击清除画布按钮时调用。

最后,我们为 canvas 元素的 mousedown、mouseup 和 mousemove 事件添加了监听器,用于开始绘制、停止绘制和实际绘制的操作。

在一些在线的脑图或者流程图示意的工具中,使用曲线连接两个模块是非常常见的效果,例如figma里面的figjam。

figjam是我用过体验非常赞的一个工具,衡量自己Web前端技术的一个简单办法就是,如果让你实现这样一个产品,你能否立刻在脑中形成实现方案,并完成之。

我就想过这个问题,如果我来实现figjam,我能行吗?

突然意识到非常不确定,所谓不确定,就是还需要摸索,对能否完成,以及完成所需的时间模糊,换言之,就还是技术积累不够。

机会总是留给有准备的人的,既然自己决定深耕于交互体验领域,就必须将自己的技术缺漏补上。

如何补,很简单,聚沙成塔,也就是从一个一个的小功能的实现开始积累。

所以,我决定先研究下给任意两个点,这两个点使用贝塞尔曲线连接该如何实现。

一、任意两点之间的曲线

需求为:给定起点和终点,然后自动得到曲线

如果你使用过PS中的钢笔工具绘制过曲线,那么对里面提到的“控制点”也应该非常熟悉,基本上,脑中只要想一下起止点和控制点,曲线什么样子脑中就自动出现了,因为控制点和定位点的连线本质上是曲线的切线,所以并不难脑补。

现在起止点有了,只要知道控制点的位置,曲线自然就得到了。

理论上,控制点可以在任意位置,这就导致曲线可能千千万,所以,我们需要进行约束,增加限定条件,比方说曲线的切线是垂直的,两个控制点的垂直坐标是一致的(不一致也可以),此时的曲线效果就会如下图所示(黑色圆点是控制点):

此时,控制点的位置就可以确定了,横坐标和起点或终点的横坐标一致,纵坐标在起始点之间(偏差越大,曲线曲率越大)。

算法知道了,下面就是落地成代码……

二、canvas中的曲线绘制

SVG和canvas都能绘制曲线,从易用性上讲,还是canvas更合适,其提供了原生的曲线绘制方法。

包括二次贝塞尔曲线方法 quadraticCurveTo() 和三次贝塞尔曲线方法bezierCurveTo()。

我们这里使用的是 context.bezierCurveTo()方法,语法为:

context.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);

其中,cp1和cp2指的是两个控制点,(x, y)是结束点,起点使用 context.moveTo(x, y) 语句指定,详见我写的这个API文档

于是我们就可以抽象出如下所示的绘制代码(假设canvas绘制的上下文对象是 context):

var drawCurve = function (startX, startY, endX, endY) {   
    // 曲线控制点坐标
    var cp1x = startX;
    var cp1y = startY + (endY - startY) / 2;
    // 这里的除数2和曲线的曲率相关,数值绝大,曲率越小
    var cp2x = endX;
    var cp2y = endY - (endY - startY) / 2;
    
    // 开始绘制曲线
    context.beginPath();
    context.lineWidth = 4;
    context.strokeStyle = '#000';
    context.moveTo(startX, startY);
    // 绘制曲线点
    context.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, endX, endY);
    context.stroke();
};

绘制曲线,然后描边。

三、最终的实现演示

好,至于方块的绘制和拖拽,这个我早就驾轻就熟了,所以,至此,我就能确信自己可以把此交互实现了,且大概多久实现也有了预期,这样,工时评估的时候就会更加准确,日常工作也就更加从容。

所谓心中有粮,自然不慌,哦,查了下,记错了,是家中有粮,心中不慌。😅

以下就是最终实现,您可以狠狠地点击这里:JS canvas任意方块图形之间曲线连接demo

拖拽方块,可以看到曲线实时跟随,此交互效果移动端也支持,下图为GIF录屏演示:

var $table = $('.dataTable').DataTable({
	......
	columns: [
		{
			title: 'Action',
			render: function (value, type, row, meta) {
				return '<a href="javascript:;" class="editBtn">Edit</a>';
			}
		},
		......
	],
	......
}).on('click', 'td>.editBtn', function () {
	var tr = $(this).parents('tr');
	var row = $table.row(tr);
	var rowData = row.data();
	......
});

function isSupportCss3() {
	//创建一个 element
	var css3Div = document.createElement('div');
	//判断是否支持圆角
	if ('border-radius' in css3Div.style) {
		//赋值css3属性
		css3Div.style['border-radius'] = '3px';
		//检测css3属性赋值是否成功
		return css3Div.style['border-radius'] == '3px';
	}
	else {
		return false;
	}
}

JavaScript 扩展 String 属性,实现 endsWith、startsWith、trim、trimEnd、trimStart和toBytes。

//左匹配
String.prototype.endsWith = function (suffix) {
    return (this.substr(this.length - suffix.length) === suffix);
}
//右匹配
String.prototype.startsWith = function (prefix) {
    return (this.substr(0, prefix.length) === prefix);
}
//去前后空格
String.prototype.trim = function () {
    return this.replace(/^\s+|\s+$/g, '');
}
//去结尾空格
String.prototype.trimEnd = function () {
    return this.replace(/\s+$/, '');
}
//去前面空格
String.prototype.trimStart = function () {
    return this.replace(/^\s+/, '');
}
//转byte数组
String.prototype.toBytes = function () {
    var ch, st, re = [];
    for (var i = 0; i < this.length; i++) {
        ch = this.charCodeAt(i);  // get char   
        st = [];                 // set up "stack"  
        do {
            st.push(ch & 0xFF);  // push byte to stack  
            ch = ch >> 8;          // shift value down by 1 byte  
        }
        while (ch);
        // add stack contents to result  
        // done because chars have "wrong" endianness  
        re = re.concat(st.reverse());
    }
    // return an array of bytes  
    return re;
}

js 格式化数字,输出格式化后的字符串。

Number.prototype.format = function (format) {
    if (!format) {
        return this.toString();
    }
    var comma = ',',
                dec = '.',
                i18n = false,
                neg = v < 0;

    var v = Math.abs(this);
    if (format.substr(format.length - 2) == '/i') {
        format = format.substr(0, format.length - 2);
        i18n = true;
        comma = '.';
        dec = ',';
    }

    var hasComma = format.indexOf(comma) != -1,
                psplit = (i18n ? format.replace(/[^\d\,]/g, '') : format.replace(/[^\d\.]/g, '')).split(dec);

    if (1 < psplit.length) {
        v = v.toFixed(psplit[1].length);
    } else if (2 < psplit.length) {
        throw ('NumberFormatException: invalid format, formats should have no more than 1 period: ' + format);
    } else {
        v = v.toFixed(0);
    }

    var fnum = v.toString();

    psplit = fnum.split('.');

    if (hasComma) {
        var cnum = psplit[0],
                    parr = [],
                    j = cnum.length,
                    m = Math.floor(j / 3),
                    n = cnum.length % 3 || 3,
                    i;

        for (i = 0; i < j; i += n) {
            if (i != 0) {
                n = 3;
            }

            parr[parr.length] = cnum.substr(i, n);
            m -= 1;
        }
        fnum = parr.join(comma);
        if (psplit[1]) {
            fnum += dec + psplit[1];
        }
    } else {
        if (psplit[1]) {
            fnum = psplit[0] + dec + psplit[1];
        }
    }

    return (neg ? '-' : '') + format.replace(/[\d,?\.?]+/, fnum);
}

利用 SheetJS js-xlsx 读取 Execl 数据转为JSON

<script type="text/javascript" src="Script/jquery.min.js"></script>
<script type="text/javascript" src="Script/js-xlsx/shim.min.js"></script>
<script type="text/javascript" src="Script/js-xlsx/xlsx.full.min.js"></script>
<script type="text/javascript">
	$(function () {
		$('.file-upload').change(function () {
			var $self = $(this);
			if (!this.files.length) {
				return;
			}
			//利用 FileReader 读取用户选中的Execl文件
			var reader = new FileReader();
			reader.onload = function (e) {
				var data = new Uint8Array(e.target.result);
				var workbook = XLSX.read(data, { type: 'array' });
				//获取第一个 Sheet 名称
				var sheetName = workbook.SheetNames[0];
				//获取第一个 Sheet
				var sheet = workbook.Sheets[sheetName];
				//转换为JSON格式数组
				var sheetJSONData = XLSX.utils.sheet_to_json(sheet);
			};
			reader.readAsArrayBuffer(this.files[0]);
		});
	});
</script>

相关文件下载

扩展jquery.fn,增加获取或设置zIndex的function

/*扩展 jQuery.fn 的方法*/
jQuery.fn.extend({
    /*
    获取或设置zIndex
    */
    zIndex: function (zIndex) {
        if (zIndex !== undefined) {
            return this.css("zIndex", zIndex);
        }
        if (this.length) {
            var elem = $(this[0]), position, value;
            while (elem.length && elem[0] !== document) {
                // Ignore z-index if position is set to a value where z-index is ignored by the browser
                // This makes behavior of this function consistent across browsers
                // WebKit always returns auto if the element is positioned
                position = elem.css("position");
                if (position === "absolute" || position === "relative" || position === "fixed") {
                    // IE returns 0 when zIndex is not specified
                    // other browsers return a string
                    // we ignore the case of nested elements with an explicit value of 0
                    // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
                    value = parseInt(elem.css("zIndex"), 10);
                    if (!isNaN(value) && value !== 0) {
                        return value;
                    }
                }
                elem = elem.parent();
            }
        }
        return 0;
    }
});