王新阳

wangxinyang

ip地址范围与cidr块互转,判断ip地址是否在给定范围或cidr块中

php版

<?php

class Ip_util{
	/**
	 * 将IP地址范围转换为CIDR块。
	 *
	 * @param string $range 格式为 "start_ip-end_ip" 的字符串。
	 * @return string[] 返回一个包含CIDR格式字符串的数组 (e.g., ["192.168.1.0/24"])。
	 */
	public static function rangeToCIDR($range) {
		list($startIp, $endIp) = explode('-', trim($range));

		$start = ip2long($startIp);
		$end = ip2long($endIp);

		if ($start === false || $end === false || $start > $end) {
			throw new InvalidArgumentException("Invalid IP range: $range");
		}

		$results = [];

		while ($start <= $end) {
			// 寻找以 $start 为起点的最大网络块
			$mask = 32;
			for ($i = 1; $i <= 32; $i++) {
				$blockSize = 1 << $i;
				$maskVal = ~($blockSize - 1);
				$blockStart = $start & $maskVal;

				// 如果这个块的起始地址小于当前start(说明对齐失败),
				// 或者这个块的结束地址超过了范围的结束地址,则不能选择此块
				if ($blockStart < $start || ($blockStart + $blockSize - 1) > $end) {
					break;
				}
				// 否则,这个块是有效的,更新掩码
				$mask = 32 - $i;
			}

			$results[] = long2ip($start) . '/' . $mask;
			$start += 1 << (32 - $mask);
		}

		return $results;
	}

	/**
	 * 将CIDR块转换为IP地址范围。
	 *
	 * @param string $cidr 格式为 "ip_address/prefix_length" 的字符串。
	 * @return string 返回一个IP范围格式的字符串 (e.g., "192.168.1.0-192.168.1.255")。
	 */
	public static function cidrToRange($cidr) {
		list($ip, $prefixLength) = explode('/', trim($cidr));
		
		$ipNum = ip2long($ip);
		
		if ($ipNum === false || !is_numeric($prefixLength) || $prefixLength < 0 || $prefixLength > 32) {
			throw new InvalidArgumentException("Invalid CIDR format: $cidr");
		}
		
		$prefixLength = (int)$prefixLength;

		$netMask = ~((1 << (32 - $prefixLength)) - 1);
		$networkAddress = $ipNum & $netMask;

		$hostBits = 32 - $prefixLength;
		$totalAddressesInSubnet = pow(2, $hostBits);
		$broadcastAddress = $networkAddress + $totalAddressesInSubnet - 1;

		$startIp = long2ip($networkAddress);
		$endIp = long2ip($broadcastAddress);

		return "$startIp-$endIp";
	}

	/**
	 * 检查IP是否在CIDR列表中
	 * @param string $ip
	 * @param array $cidrList
	 * @return bool
	 */
	public static function isInCidrList($ip, $cidrList) {
		$ipLong = ip2long($ip);
		if ($ipLong === false) {
			return false; // 无效IP
		}

		foreach ($cidrList as $cidr) {
			list($networkStr, $prefixLen) = explode('/', trim($cidr));
			$prefixLen = (int)$prefixLen;
			
			if (!is_numeric($prefixLen) || $prefixLen < 0 || $prefixLen > 32) {
				continue; // 跳过无效CIDR
			}

			$networkLong = ip2long($networkStr);
			if ($networkLong === false) {
				continue; // 跳过无效网络地址
			}

			// 计算网络掩码
			$mask = ~((1 << (32 - $prefixLen)) - 1);
			
			// 检查IP和网络地址的网络部分是否相同
			if (($ipLong & $mask) === ($networkLong & $mask)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 检查IP是否在IP范围列表中
	 * @param string $ip
	 * @param array $rangeList
	 * @return bool
	 */
	public static function isInRangeList($ip, $rangeList) {
		$ipLong = ip2long($ip);
		if ($ipLong === false) {
			return false; // 无效IP
		}

		foreach ($rangeList as $range) {
			list($startStr, $endStr) = explode('-', trim($range));

			$startLong = ip2long($startStr);
			$endLong = ip2long($endStr);

			if ($startLong === false || $endLong === false) {
				continue; // 跳过无效范围
			}

			// 检查IP是否在范围内
			if ($ipLong >= $startLong && $ipLong <= $endLong) {
				return true;
			}
		}
		return false;
	}
}







// --- 示例用法 ---
$ranges = [
    '192.168.1.0-192.168.1.255',
    '192.168.2.0-192.168.5.0',
    '192.168.2.0-192.168.5.128',
    '192.168.2.0-192.168.5.255',
    '192.168.0.0-192.168.255.255',
    '192.1.127.0-192.5.127.255',
    '192.1.127.0-192.5.128.169',
    '192.1.127.0-192.5.127.0'
];

$cidrs=[];

foreach ($ranges as $range) {
    echo "输入范围: $range\n";
    $result = Ip_util::rangeToCIDR($range);
	$cidrs=array_merge($cidrs,$result);
    echo "输出CIDR(s): [" . implode(', ', array_map(function($c) { return "\"$c\""; }, $result)) . "]\n\n";
	echo "逆推:\n";
	foreach($result as $row){
		echo Ip_util::cidrToRange($row),"\n";
	}
	echo "\n\n";
}

$targetIp='192.5.128.150';


echo "目标IP: $targetIp\n\n";

echo "检查IP是否在范围列表中:\n";
foreach ($ranges as $range) {
    $inRange = Ip_util::isInRangeList($targetIp, [$range]);
    echo "  - '$range': " . ($inRange ? "是" : "否") . "\n";
}
echo "总体结果 (在任一范围内): " . (Ip_util::isInRangeList($targetIp, $ranges) ? "是" : "否") . "\n\n";

echo "检查IP是否在CIDR列表中:\n";
foreach ($cidrs as $cidr) {
    $inCidr = Ip_util::isInCidrList($targetIp, [$cidr]);
    echo "  - '$cidr': " . ($inCidr ? "是" : "否") . "\n";
}
echo "总体结果 (在任一CIDR中): " . (Ip_util::isInCidrList($targetIp, $cidrs) ? "是" : "否") . "\n";


?>

js版

<script>
/**
 * IP范围转CIDR列表
 * @param {string} ipRange
 * @returns {string[]}
 */
function rangeToCIDR(ipRange) {
	const [startIp, endIp]=ipRange.split('-');
    const ipToInt = ip => ip.split('.').reduce((acc, x) => (acc << 8) + (+x), 0);
    const intToIp = int => [24, 16, 8, 0].map(s => (int >> s) & 0xFF).join('.');
    
    let start = ipToInt(startIp);
    const end = ipToInt(endIp);
    const results = [];
    
    while (start <= end) {
        // 找到start的最低位1的位置
        let mask = 32;
        for (let i = 1; i <= 32; i++) {
            const blockSize = 1 << i;
            const maskVal = ~(blockSize - 1);
            const blockStart = start & maskVal;
            
            if (blockStart < start || blockStart + blockSize - 1 > end) {
                break;
            }
            mask = 32 - i;
        }
        
        results.push(`${intToIp(start)}/${mask}`);
        start += 1 << (32 - mask);
    }
    
    return results;
}

/**
 * CIDR转IP范围
 * @param {string} cidr
 * @returns {string}
 */
function cidrToRange(cidr) {
    const ipToInt = ip => ip.split('.').reduce((acc, octet, i) => acc + (parseInt(octet) << (24 - i * 8)), 0);
    const intToIp = int => [24, 16, 8, 0].map(shift => (int >>> shift) & 0xFF).join('.');
    
    const [ip, prefixStr] = cidr.split('/');
    const prefix = parseInt(prefixStr, 10);
    const mask = ~0 << (32 - prefix);
    const ipInt = ipToInt(ip);
    const network = ipInt & mask;
    
	return intToIp(network)+'-'+intToIp(network + (1 << (32 - prefix)) - 1);
    return {
        start: intToIp(network),
        end: intToIp(network + (1 << (32 - prefix)) - 1)
    };
}

/**
 * 检查IP是否在CIDR列表中
 * @param {string} ip
 * @param {string[]} cidrList
 * @returns {boolean}
 */
function isIpInCidrList(ip, cidrList) {
    const ipLong = ip.split('.').reduce((acc, x) => (acc << 8) + (+x), 0) >>> 0;
    if (isNaN(ipLong)) return false; // 无效IP

    for (const cidr of cidrList) {
        const [networkStr, prefixLenStr] = cidr.split('/');
        const prefixLen = parseInt(prefixLenStr, 10);
        
        if (isNaN(prefixLen) || prefixLen < 0 || prefixLen > 32) {
            continue; // 跳过无效CIDR
        }

        const networkLong = networkStr.split('.').reduce((acc, x) => (acc << 8) + (+x), 0) >>> 0;
        if (isNaN(networkLong)) {
            continue; // 跳过无效网络地址
        }

        const mask = ~((1 << (32 - prefixLen)) - 1) >>> 0;
        
        if ((ipLong & mask) === (networkLong & mask)) {
            return true;
        }
    }
    return false;
}

/**
 * 检查IP是否在IP范围列表中
 * @param {string} ip
 * @param {string[]} rangeList
 * @returns {boolean}
 */
function isIpInRangeList(ip, rangeList) {
    const ipLong = ip.split('.').reduce((acc, x) => (acc << 8) + (+x), 0) >>> 0;
    if (isNaN(ipLong)) return false; // 无效IP

    for (const range of rangeList) {
        const [startStr, endStr] = range.split('-');

        const startLong = startStr.split('.').reduce((acc, x) => (acc << 8) + (+x), 0) >>> 0;
        const endLong = endStr.split('.').reduce((acc, x) => (acc << 8) + (+x), 0) >>> 0;

        if (isNaN(startLong) || isNaN(endLong)) {
            continue; // 跳过无效范围
        }

        if (ipLong >= startLong && ipLong <= endLong) {
            return true;
        }
    }
    return false;
}



// --- 示例用法 ---
const ranges = [
    '192.168.1.0-192.168.1.255',
    '192.168.2.0-192.168.5.0',
    '192.168.2.0-192.168.5.128',
    '192.168.2.0-192.168.5.255',
    '192.168.0.0-192.168.255.255',
    '192.1.127.0-192.5.127.255',
    '192.1.127.0-192.5.128.169',
    '192.1.127.0-192.5.127.0'
];
cidrs=[];

ranges.forEach(range => {
    console.log(`输入范围: ${range}`);
    const result = rangeToCIDR(range);
	cidrs=cidrs.concat(result);
    console.log(`输出CIDR(s): [${result.map(c => `"${c}"`).join(', ')}]\n`);
	console.log("逆推:\n");
	for(let index in result){
		console.log(cidrToRange(result[index])+"\n");
	}
/*
	$.each(result,function(index,row){
		console.log(cidrToRange(row)+"\n");
	});
*/
	console.log("\n\n");
});

const targetIp = "192.5.128.150";
console.log(`目标IP: ${targetIp}\n`);

console.log("检查IP是否在范围列表中:");
ranges.forEach(range => {
    const inRange = isIpInRangeList(targetIp, [range]);
    console.log(`  - '${range}': ${inRange ? '是' : '否'}`);
});
console.log(`总体结果 (在任一范围内): ${isIpInRangeList(targetIp, ranges) ? '是' : '否'}\n`);

console.log("检查IP是否在CIDR列表中:");
cidrs.forEach(cidr => {
    const inCidr = isIpInCidrList(targetIp, [cidr]);
    console.log(`  - '${cidr}': ${inCidr ? '是' : '否'}`);
});
console.log(`总体结果 (在任一CIDR中): ${isIpInCidrList(targetIp, cidrs) ? '是' : '否'}`);
</script>

js实现浏览器全屏切换

调用浏览器的 Fullscreen API 来实现点击按钮后让整个页面(或指定元素)进入全屏模式

var btn=document.getElementById('fullscreenBtn');
btn.addEventListener('click', function(){
	if (document.fullscreenElement && document.exitFullscreen) {
		document.exitFullscreen();
		return;
	}

	// 获取整个文档根元素(通常是 )
	const elem = document.documentElement;

	// 尝试进入全屏(兼容不同浏览器前缀)
	if (elem.requestFullscreen) {
		elem.requestFullscreen();
	} else if (elem.mozRequestFullScreen) { /* Firefox */
		elem.mozRequestFullScreen();
	} else if (elem.webkitRequestFullscreen) { /* Chrome, Safari, Edge */
		elem.webkitRequestFullscreen();
	} else if (elem.msRequestFullscreen) { /* IE11 */
		elem.msRequestFullscreen();
	} else {
		alert('您的浏览器不支持全屏功能');
	}
});

阻止事件冒泡的几种方法

如下代码,双击图片区域时,也会触发 alert(0) 事件,要阻止双击图片的冒泡

<body>
<img src="a.JPG" width="50%">
<script>
document.addEventListener('dblclick', function(e) {
    e.preventDefault(); // 阻止默认行为
    alert(0);
});
</script>
</body>

方法1:阻止图片的默认双击行为

<body>
<img src="a.JPG" width="50%" ondblclick="event.preventDefault();">
<script>
document.addEventListener('dblclick', function(){
    alert(0);
});
</script>
</body>

方法2:直接在图片上添加事件监听器(推荐)

<body>
<img src="a.JPG" width="50%" id="myImage">
<script>
document.getElementById('myImage').addEventListener('dblclick', function(event){
    event.preventDefault(); // 阻止默认行为
    alert(0);
}, false);
</script>
</body>

方法3:使用事件捕获阶段

<body>
<img src="a.JPG" width="50%">
<script>
document.addEventListener('dblclick', function(){
    alert(0);
}, true); // 使用捕获阶段而非冒泡阶段
</script>
</body>

方法4:完全阻止图片的所有默认行为

<body>
<img src="a.JPG" width="50%" style="pointer-events: none;">
<script>
document.addEventListener('dblclick', function(){
    alert(0);
});
</script>
</body>

方法5:使用更具体的事件处理(推荐)

<body>
<img src="a.JPG" width="50%">
<script>
// 捕获图片的双击事件
document.querySelector('img').addEventListener('dblclick', function(e){
    e.stopPropagation(); // 阻止冒泡到document
    alert('双击了图片!');
});

// 其他地方的双击事件
document.addEventListener('dblclick', function(e){
    if(e.target.tagName !== 'IMG') {
        alert('双击了非图片区域!');
    }
});
</script>
</body>

浏览器全屏切换、不允许选中示例

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>浏览器全屏切换示例</title>
<style>
.no-select{-webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none;}
</style>
</head>
<body>
<button id="fullscreenBtn">进入全屏模式</button>
<div class="no-select" style="margin:2em 0;">使用css控制,双击这里时,文字<span style="color:red">不会</span>被选中</div>
<div style="margin:2em 0;">双击这里时,文字<span style="color:red">会</span>被选中</div>

<strong>css控制不选中</strong>
<pre style="padding:10px;border:1px solid #ccc;background-color:#f3f3f3">
.no-select{
	-webkit-user-select:none;
	-moz-user-select:none;
	-ms-user-select:none;
	user-select:none;
}
</pre>
<strong>js控制不选中</strong>
<pre style="padding:10px;border:1px solid #ccc;background-color:#f3f3f3">
	const selection = window.getSelection();
	if(selection)selection.removeAllRanges();
</pre>

<script>
function toggleFullscreen() {
	// 获取全屏API的兼容性写法
	const requestFullscreen = document.documentElement.requestFullscreen || 
		document.documentElement.mozRequestFullScreen ||
		document.documentElement.webkitRequestFullscreen ||
		document.documentElement.msRequestFullscreen;

	const exitFullscreen = document.exitFullscreen ||
		document.mozCancelFullScreen ||
		document.webkitExitFullscreen ||
		document.msExitFullscreen;
	if (!document.fullscreenElement &&
		!document.mozFullScreenElement &&
		!document.webkitFullscreenElement &&
		!document.msFullscreenElement) {
		// 进入全屏
		requestFullscreen.call(document.documentElement);
	} else {
		// 退出全屏
		exitFullscreen.call(document);
	}
}

document.getElementById('fullscreenBtn').addEventListener('click', toggleFullscreen);
document.addEventListener('dblclick', toggleFullscreen);
</script>
</body>
</html>

html跨平台在线预览pdf

PDFObject.js

官网:https://pdfobject.com/

相关博客:

https://blog.csdn.net/i_dont_know_a/article/details/80707963

https://blog.csdn.net/qappleh/article/details/80250492

https://www.jq22.com/yanshi649


pdf.js

官网:https://mozilla.github.io/pdf.js/getting_started/

相关博客:

https://blog.csdn.net/weixin_42400643/article/details/152602790

https://blog.csdn.net/weixin_31800911/article/details/148820919

https://www.jb51.net/javascript/338851frl.htm

移动端h5常用的几款插件

1、拖拽排序插件——Sortable.js
演示地址:https://www.jq22.com/yanshi10850


2、日期选择插件——rolldate.js
演示地址:https://weijhfly.github.io/rolldate-index.html
下载地址:https://www.jq22.com/jquery-info19834


3、图片裁剪插件——cropper.js
下载地址:https://github.com/fengyuanchen/cropperjs


4、底部滑动选择插件——mobileSelect.js
下载地址:https://www.jq22.com/jquery-info14679
演示地址:https://www.jq22.com/yanshi14679

layui.prompt 监听回车键来触发确认操作

layer.prompt({
	title: '请输入密码:',
	formType: 1,
	success: function(layero, index){
		$(layero).find('.layui-layer-input').prop('autocomplete','new-password').on('keydown',function(e){
			if(e.which === 13){
				$(layero).find('.layui-layer-btn0').trigger('click');
			}
		});
	},
	yes: function(index, layero){
		let elem=$(layero).find('.layui-layer-input'), value=elem.val();
		if(value==='123456'){
			layer.close(index);
			layer.msg('密码正确');
		}else{
			elem.focus();
			layer.tips(value===''?'请输入':'密码错误',elem,{tips:1,time:2e3});
		}
	}
});

传统prompt方法无法监听输入

layer.prompt({
	title: '请输入密码:',
	formType: 1,
},function(value, index, elem){
	console.log(value);
	layer.close(index);
});

js加载远程图片转为base64格式

分别用js、php实现WGS84与GCJ02两种坐标系的互转

高德地图、腾讯地图: 使用 GCJ-02 坐标系,也称为火星坐标系。它是基于国际标准 WGS-84 坐标系进行加密处理得到的。
百度地图: 采用 BD-09 坐标系,这是在 GCJ-02 基础上进行二次加密而来的,具有更高的安全性和隐私保护。
天地图: 使用 CGCS2000 坐标系,该坐标系与 WGS-84 存在微小偏差,在要求不高的情况下可以直接与 WGS-84 互换使用。

js

/**
 * WGS84转GCJ02(火星坐标系)
 * @param {number} wgsLon WGS84坐标系的经度
 * @param {number} wgsLat WGS84坐标系的纬度
 * @returns {Array} GCJ02坐标 [经度, 纬度]
 */
function wgs84ToGcj02(wgsLon, wgsLat) {
    const PI = 3.14159265358979324;
    const a = 6378245.0; //长半轴
    const ee = 0.00669342162296594323; //扁率
    
    if (outOfChina(wgsLat, wgsLon)) {
        return [wgsLon, wgsLat];
    }
    
    let dLat = transformLat(wgsLon - 105.0, wgsLat - 35.0);
    let dLon = transformLon(wgsLon - 105.0, wgsLat - 35.0);
    
    const radLat = wgsLat / 180.0 * PI;
    let magic = Math.sin(radLat);
    magic = 1 - ee * magic * magic;
    const sqrtMagic = Math.sqrt(magic);
    
    dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * PI);
    dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * PI);
    
    const gcjLat = wgsLat + dLat;
    const gcjLon = wgsLon + dLon;
    
    return [gcjLon, gcjLat];
}

/**
 * GCJ02(火星坐标系)转WGS84
 * @param {number} gcjLon GCJ02坐标系的经度
 * @param {number} gcjLat GCJ02坐标系的纬度
 * @returns {Array} WGS84坐标 [经度, 纬度]
 */
function gcj02ToWgs84(gcjLon, gcjLat) {
    const PI = 3.14159265358979324;
    const a = 6378245.0;
    const ee = 0.00669342162296594323;
    
    if (outOfChina(gcjLat, gcjLon)) {
        return [gcjLon, gcjLat];
    }
    
    let dLat = transformLat(gcjLon - 105.0, gcjLat - 35.0);
    let dLon = transformLon(gcjLon - 105.0, gcjLat - 35.0);
    
    const radLat = gcjLat / 180.0 * PI;
    let magic = Math.sin(radLat);
    magic = 1 - ee * magic * magic;
    const sqrtMagic = Math.sqrt(magic);
    
    dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * PI);
    dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * PI);
    
    const wgsLat = gcjLat - dLat;
    const wgsLon = gcjLon - dLon;
    
    return [wgsLon, wgsLat];
}

function transformLat(x, y) {
    let ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x));
    ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0;
    ret += (20.0 * Math.sin(y * Math.PI) + 40.0 * Math.sin(y / 3.0 * Math.PI)) * 2.0 / 3.0;
    ret += (160.0 * Math.sin(y / 12.0 * Math.PI) + 320 * Math.sin(y * Math.PI / 30.0)) * 2.0 / 3.0;
    return ret;
}

function transformLon(x, y) {
    let ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x));
    ret += (20.0 * Math.sin(6.0 * x * Math.PI) + 20.0 * Math.sin(2.0 * x * Math.PI)) * 2.0 / 3.0;
    ret += (20.0 * Math.sin(x * Math.PI) + 40.0 * Math.sin(x / 3.0 * Math.PI)) * 2.0 / 3.0;
    ret += (150.0 * Math.sin(x / 12.0 * Math.PI) + 300.0 * Math.sin(x / 30.0 * Math.PI)) * 2.0 / 3.0;
    return ret;
}

function outOfChina(lat, lon) {
    return lon < 72.004 || lon > 137.8347 || lat < 0.8293 || lat > 55.8271;
}

// 使用示例
// let gcjCoord = wgs84ToGcj02(116.404, 39.915); // WGS84转GCJ02
// let wgsCoord = gcj02ToWgs84(116.404, 39.915); // GCJ02转WGS84

PHP

/**
 * WGS84转GCJ02(火星坐标系)
 * @param float $wgsLon WGS84坐标系的经度
 * @param float $wgsLat WGS84坐标系的纬度
 * @return array GCJ02坐标 [经度, 纬度]
 */
function wgs84ToGcj02($wgsLon, $wgsLat) {
    $PI = 3.14159265358979324;
    $a = 6378245.0; //长半轴
    $ee = 0.00669342162296594323; //扁率
    
    if (outOfChina($wgsLat, $wgsLon)) {
        return array($wgsLon, $wgsLat);
    }
    
    $dLat = transformLat($wgsLon - 105.0, $wgsLat - 35.0);
    $dLon = transformLon($wgsLon - 105.0, $wgsLat - 35.0);
    
    $radLat = $wgsLat / 180.0 * $PI;
    $magic = sin($radLat);
    $magic = 1 - $ee * $magic * $magic;
    $sqrtMagic = sqrt($magic);
    
    $dLat = ($dLat * 180.0) / (($a * (1 - $ee)) / ($magic * $sqrtMagic) * $PI);
    $dLon = ($dLon * 180.0) / ($a / $sqrtMagic * cos($radLat) * $PI);
    
    $gcjLat = $wgsLat + $dLat;
    $gcjLon = $wgsLon + $dLon;
    
    return array($gcjLon, $gcjLat);
}

/**
 * GCJ02(火星坐标系)转WGS84
 * @param float $gcjLon GCJ02坐标系的经度
 * @param float $gcjLat GCJ02坐标系的纬度
 * @return array WGS84坐标 [经度, 纬度]
 */
function gcj02ToWgs84($gcjLon, $gcjLat) {
    $PI = 3.14159265358979324;
    $a = 6378245.0;
    $ee = 0.00669342162296594323;
    
    if (outOfChina($gcjLat, $gcjLon)) {
        return array($gcjLon, $gcjLat);
    }
    
    $dLat = transformLat($gcjLon - 105.0, $gcjLat - 35.0);
    $dLon = transformLon($gcjLon - 105.0, $gcjLat - 35.0);
    
    $radLat = $gcjLat / 180.0 * $PI;
    $magic = sin($radLat);
    $magic = 1 - $ee * $magic * $magic;
    $sqrtMagic = sqrt($magic);
    
    $dLat = ($dLat * 180.0) / (($a * (1 - $ee)) / ($magic * $sqrtMagic) * $PI);
    $dLon = ($dLon * 180.0) / ($a / $sqrtMagic * cos($radLat) * $PI);
    
    $wgsLat = $gcjLat - $dLat;
    $wgsLon = $gcjLon - $dLon;
    
    return array($wgsLon, $wgsLat);
}

function transformLat($x, $y) {
    $ret = -100.0 + 2.0 * $x + 3.0 * $y + 0.2 * $y * $y + 0.1 * $x * $y + 0.2 * sqrt(abs($x));
    $ret += (20.0 * sin(6.0 * $x * M_PI) + 20.0 * sin(2.0 * $x * M_PI)) * 2.0 / 3.0;
    $ret += (20.0 * sin($y * M_PI) + 40.0 * sin($y / 3.0 * M_PI)) * 2.0 / 3.0;
    $ret += (160.0 * sin($y / 12.0 * M_PI) + 320 * sin($y * M_PI / 30.0)) * 2.0 / 3.0;
    return $ret;
}

function transformLon($x, $y) {
    $ret = 300.0 + $x + 2.0 * $y + 0.1 * $x * $x + 0.1 * $x * $y + 0.1 * sqrt(abs($x));
    $ret += (20.0 * sin(6.0 * $x * M_PI) + 20.0 * sin(2.0 * $x * M_PI)) * 2.0 / 3.0;
    $ret += (20.0 * sin($x * M_PI) + 40.0 * sin($x / 3.0 * M_PI)) * 2.0 / 3.0;
    $ret += (150.0 * sin($x / 12.0 * M_PI) + 300.0 * sin($x / 30.0 * M_PI)) * 2.0 / 3.0;
    return $ret;
}

function outOfChina($lat, $lon) {
    return $lon < 72.004 || $lon > 137.8347 || $lat < 0.8293 || $lat > 55.8271;
}

// 使用示例
// $gcjCoord = wgs84ToGcj02(116.404, 39.915); // WGS84转GCJ02
// $wgsCoord = gcj02ToWgs84(116.404, 39.915); // GCJ02转WGS84

百度地图API在CodeIgniter中的实现:
地理编码、全球逆地理编码、坐标系转换

/**
 * 百度地图开放平台接口
 */
class Baidumap extends MY_Controller{
	public function __construct(){
		parent::__construct();
		
		//百度地图配置
		define('BAIDUMAP_CONFIG', array(
			'ak' => '百度地图AK',
			'sk' => '百度地图SK',
			'host' => 'https://api.map.baidu.com',
		));
	}
	
	//计算请求验证的SN值
	private function caculateAKSN($sk, $uri, $param, $method = 'GET'){
		if($method === 'POST'){
			ksort($param);
		}
		$querystring = http_build_query($param);
		return md5(urlencode($uri.'?'.$querystring.$sk));
	}

	/**
	 * 地理编码
	 * 为保证高德、腾讯、百度地图通用,所以获取的是国测局坐标gcj02ll
	 * https://lbsyun.baidu.com/faq/api?title=webapi/guide/webservice-geocoding-base
	 */
	public function geocoder(){
		$address=trim(G('address'));
		if(empty($address))return json(101,'参数address不能为空');
		$city=trim(G('city'));
		$city OR $city='济南市';
		
		$coord_type=G('coordtype', 'gcj02ll'); //或百度坐标bd09ll
		
		$uri = '/geocoding/v3/';
		//构造请求串数组
		$param = array(
			'address' => $address,
			'city' => $city,
			'ret_coordtype' => $coord_type,
			'output' => 'json',
			'ak' => BAIDUMAP_CONFIG['ak'],
		);

		//调用sn计算函数,默认get请求
		$param['sn'] = $this->caculateAKSN(BAIDUMAP_CONFIG['sk'], $uri, $param);
		$res=my_curl(BAIDUMAP_CONFIG['host'].$uri,'GET',$param);
		$res=json_decode($res,true);
		$res['result']['location']['lng']=(string)$res['result']['location']['lng'];
		if(!is_array($res))return json(102,'请求失败,请重试');
		if($res['status']==0){
			$data=$res['result'];
			$data['lon_lat']=$data['location']['lng'].','.$data['location']['lat'];
			$data['lat_lon']=$data['location']['lat'].','.$data['location']['lng'];
			unset($data['location']);
			
			return json(0,'',$data);
		}else{
			return json($res['status'],$res['message']);
		}
	}

	/**
	 * 坐标转换
	 * 把国测局坐标gcj02ll转为百度坐标
	 * https://lbsyun.baidu.com/faq/api?title=webapi/guide/changeposition-base
	 * coord坐标经度在前
$model 转换方式可选值:
1:amap/tencent to bd09ll
2:gps to bd09ll
3:bd09ll to bd09mc
4:bd09mc to bd09ll
5:bd09ll to amap/tencent
6:bd09mc to amap/tencent
	 */
	public function getconv(){
		$coord=str_replace(',',',',trim(G('coord')));
		if(!preg_match('/^\d+(\.\d+)?,\d+(\.\d+)?$/',$coord))return json(101,'坐标格式错误');
		$model=G('model','1');
		$model=is_id($model) ? (int)$model : 1;
		
		$uri = '/geoconv/v2/';
		
		//需转换的源坐标,多组坐标以";"分隔
		$param=array(
			'coords' => $coord,
			'model' => $model,
			'output' => 'json',
			'ak' => BAIDUMAP_CONFIG['ak'],
		);
		$param['sn'] = $this->caculateAKSN(BAIDUMAP_CONFIG['sk'], $uri, $param);

		$res = my_curl(BAIDUMAP_CONFIG['host'].$uri, 'GET', $param);
		//echo $res;
		$res = json_decode($res,true);
		if(!is_array($res))return json(102,'请求失败,请重试');
		
		if($res['status']!=0){
			return json($res['status'], $res['message']);
		}
		$data=array(
			'lon_lat' => $res['result'][0]['x'].','.$res['result'][0]['y'],
			'lat_lon' => $res['result'][0]['y'].','.$res['result'][0]['x'],
		);
		return json(0,'',$data);
	}

	/**
	 * 全球逆地理编码
	 * https://lbsyun.baidu.com/faq/api?title=webapi/guide/webservice-geocoding-abroad-base
	 * coord坐标纬度在前,格式:纬度,经度
	 */
	public function reverse_geocoder(){
		$coord=str_replace(',',',',trim(G('coord')));
		if(empty($coord))return json(101,'参数coord不能为空');
		if(!preg_match('/^\d+(\.\d+)?,\d+(\.\d+)?$/',$coord))return json(102,'坐标格式错误');
		
		$coordtype=G('coordtype', 'gcj02ll'); //或百度坐标bd09ll
		
		$uri = '/reverse_geocoding/v3';
		
		$param=array(
			'location' => $coord,
			'coordtype' => $coordtype,
			'extensions_poi' => '1',
			'radius' => 50, //poi半径:0-3000米
			'region_data_source' => 1, //行政区划数据的来源:1统计局(把开发区作为行政区划返回),2民政部
			'entire_poi' => 1,
			'sort_strategy' => 'distance',
			//'poi_types' => '交通设施|公交线路|铁路',
			'output' => 'json',
			'ak' => BAIDUMAP_CONFIG['ak'],
		);
		//printr($param);
		$param['sn'] = $this->caculateAKSN(BAIDUMAP_CONFIG['sk'], $uri, $param);
		
		$res = my_curl(BAIDUMAP_CONFIG['host'].$uri, 'GET', $param);
		$res = json_decode($res,true);
		
		if($res['status']!=0){
			return json($res['status'], $res['message']);
		}
		$result=$res['result'];
		$data=array(
			'formatted_address' => $result['formatted_address'],
			'formatted_address_poi' => $result['formatted_address_poi'],
			'addressComponent' => $result['addressComponent'],
		);
		json(0,'',$data);
	}
}

js保存剪切板中的截图

来源:deepseek

<!DOCTYPE html>
<html>
<head>
    <title>保存截图工具</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
        }
        .container {
            border: 2px dashed #ccc;
            padding: 20px;
            text-align: center;
            margin-top: 20px;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <h1>截图保存工具</h1>
    <p>1. 使用微信或其他截图工具截取屏幕</p>
    <p>2. 点击下方区域后按Ctrl+V(Windows)或Command+V(Mac)粘贴</p>
    <p>3. 图片将自动下载</p>
    
    <div class="container" id="pasteArea">
        点击此处后粘贴截图
    </div>

    <script>
        const pasteArea = document.getElementById('pasteArea');
        
        pasteArea.addEventListener('click', function() {
            pasteArea.textContent = '请按Ctrl+V/Command+V粘贴截图...';
            // 只添加一次性的监听器
            document.addEventListener('paste', handlePaste, { once: true });
        });

        function handlePaste(event) {
            const items = (event.clipboardData || window.clipboardData).items;
            
            for (let i = 0; i < items.length; i++) {
                if (items[i].type.indexOf('image') !== -1) {
                    const blob = items[i].getAsFile();
                    if (blob) {
                        saveImage(blob);
                        alert('截图已保存!');
                    }
                    event.preventDefault();
                    pasteArea.textContent = '截图已处理,如需再次保存请点击此处';
                    return;
                }
            }
            
            alert('未检测到图片,请确保已复制截图图像');
            pasteArea.textContent = '截图未识别,请点击此处重试';
        }

        function saveImage(blob) {
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            
            const now = new Date();
            const timestamp = `${now.getFullYear()}${(now.getMonth()+1).toString().padStart(2, '0')}${now.getDate().toString().padStart(2, '0')}_${now.getHours().toString().padStart(2, '0')}${now.getMinutes().toString().padStart(2, '0')}${now.getSeconds().toString().padStart(2, '0')}`;
            a.download = `screenshot_${timestamp}.png`;
            
            document.body.appendChild(a);
            a.click();
            
            setTimeout(() => {
                document.body.removeChild(a);
                window.URL.revokeObjectURL(url);
            }, 100);
        }
    </script>
</body>
</html>
2026-03-13 星期五 农历正月二十五