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常用的几款插件
演示地址: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、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>