找到
164
篇与
站长可乐
相关的结果
-
发现一个有趣的便签墙网站 发现一个不错的网站,这个网站作者是:https://github.com/uninto/notes,纯html、css、js实现。 有人说这个类似之前的许愿墙,我觉得也可以把这个元素加进去,毕竟我群里很多宅男腐女,最喜欢这个玩意儿了,晚些我直接上线到我的工具箱里 mhgb3msq.png图片 因为这个只有一个前端页面,代码如下,我就准备做个后端出来,到时候重新开个帖子,供大家使用。 <html lang="zh-CN"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>便签墙</title> <style> * { box-sizing: border-box; margin: 0; padding: 0; } body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background-image: linear-gradient(0deg, #eee 1px, transparent 0), linear-gradient(90deg, #eee 1px, transparent 0); background-size: 30px 30px; color: #333; min-height: 100dvh; overflow: hidden; } body.has-maximized-card { overflow: hidden; } body.is-mobile { overflow-y: auto; } #board { position: relative; width: 100vw; height: 100dvh; overflow: hidden; } body.is-mobile #board { height: auto; min-height: 100dvh; } .card { position: absolute; width: 220px; border-radius: 12px; box-shadow: 0 16px 35px rgba(0, 0, 0, 0.2); background: #fff; border: 1px solid rgba(0, 0, 0, 0.08); overflow: hidden; opacity: 0; transform-origin: center; transition: transform 0.35s ease, opacity 0.35s ease, left 0.35s ease, top 0.35s ease, width 0.35s ease, height 0.35s ease, border-radius 0.35s ease; } .card.dragging { transition: none; box-shadow: 0 22px 45px rgba(0, 0, 0, 0.35); } .card.maximized { position: fixed; inset: 0; width: 100vw; height: 100vh; height: 100dvh; border-radius: 0; box-shadow: 0 28px 60px rgba(0, 0, 0, 0.4); } .card-header { display: flex; align-items: center; justify-content: space-between; padding: 10px 12px; background: rgba(255, 255, 255, 0.7); cursor: grab; user-select: none; touch-action: pan-y; } .card-header.dragging { cursor: grabbing; } .window-controls { display: flex; align-items: center; gap: 6px; } .window-controls .control { position: relative; width: 12px; height: 12px; border-radius: 50%; border: 1px solid rgba(0, 0, 0, 0.08); background: #ccc; cursor: pointer; outline: none; padding: 0; display: inline-flex; align-items: center; justify-content: center; } .window-controls .control.close { background: #ff5f57; border-color: #e0443e; } .window-controls .control.minimize { background: #febb2e; border-color: #dea123; } .window-controls .control.maximize { background: #28c840; border-color: #1aab2c; } .window-controls .control::after { content: ''; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); opacity: 0; transition: opacity 0.2s ease; } .card-header:hover .window-controls .control::after { opacity: 0.8; } .window-controls .control.close::after { content: '×'; width: auto; height: auto; background: none; font-size: 10px; line-height: 1; font-weight: 700; color: rgba(0, 0, 0, 0.7); } .window-controls .control.minimize::after { width: 6px; height: 2px; background: rgba(0, 0, 0, 0.6); } .window-controls .control.maximize::after { width: 6px; height: 6px; background: linear-gradient( 45deg, rgba(0, 0, 0, 0.6) 0%, rgba(0, 0, 0, 0.6) 45%, transparent 45%, transparent 55%, rgba(0, 0, 0, 0.6) 55%, rgba(0, 0, 0, 0.6) 100% ); } .card-title { font-size: 13px; font-weight: 600; color: rgba(0, 0, 0, 0.55); padding-left: 10px; flex: 1; } .card-body { padding: 16px; font-size: 16px; line-height: 1.4; font-weight: 600; color: rgba(0, 0, 0, 0.72); word-break: break-word; overflow-wrap: anywhere; white-space: normal; } .card.maximized { display: flex; flex-direction: column; } .card.maximized .card-title { display: none; } .card.maximized .card-body { flex: 1; display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; text-align: center; padding: clamp(32px, min(10vw, 10vh), 128px); padding-top: clamp(72px, min(14vw, 14vh), 192px); font-size: clamp(48px, min(18vw, 18vh), 200px); line-height: 1.05; } @media (max-width: 768px) { .card { width: 180px; border-radius: 10px; } .card-body { padding: 14px; font-size: 14px; } .card-title { font-size: 12px; } } </style> </head> <body> <div id="board"></div> <script> const board = document.getElementById('board') const messages = [ '保持好心情', '多喝水哦', '今天辛苦啦', '早点休息', '记得吃水果', '加油,你可以的', '祝你顺利', '保持微笑呀', '愿所有烦恼都消失', '期待下一次见面', '梦想总会实现', '天气冷了,多穿衣服', '记得给自己放松', '每天都要元气满满', '今天也要好好爱自己', '适当休息一下' ] const colors = [ '#ffe0e3', '#c7f0ff', '#ffd8a8', '#d9f2d9', '#e5d7ff', '#f9f7d9', '#d2f0f8', '#ffd4f5' ] const cardStates = new WeakMap() // Reserve a very high层级给全屏卡片,避免被后续元素覆盖 const MAXIMIZED_LAYER = 1000000 let activeMaximizedCard = null const pointerMediaQuery = window.matchMedia('(pointer: coarse)') let isMobile = pointerMediaQuery.matches || window.innerWidth <= 768 let maxCards = isMobile ? 120 : 180 // 限制 DOM 节点数量,减轻移动端压力 const initialCardCount = isMobile ? 18 : 30 let spawnInterval = isMobile ? 700 : 400 let zIndexCursor = 200 let spawnTimer = null document.body.classList.toggle('is-mobile', isMobile) function randomFrom(array) { return array[Math.floor(Math.random() * array.length)] } function clamp(value, min, max) { return Math.min(Math.max(value, min), max) } function applyTransform(card, state) { const scale = state.scale ?? 1 const angle = state.angle ?? 0 card.style.transform = `scale(${scale}) rotate(${angle}deg)` } function bringToFront(card) { if (card === activeMaximizedCard) { card.style.zIndex = MAXIMIZED_LAYER return } zIndexCursor += 1 if (activeMaximizedCard && zIndexCursor >= MAXIMIZED_LAYER) { zIndexCursor = MAXIMIZED_LAYER - 1 } card.style.zIndex = zIndexCursor } function updateBodyMaximizedState() { document.body.classList.toggle( 'has-maximized-card', Boolean(activeMaximizedCard) ) } function scheduleNextSpawn() { clearTimeout(spawnTimer) spawnTimer = setTimeout(() => { if (!document.hidden) { createCard() } scheduleNextSpawn() }, spawnInterval) } function syncMobileMode() { const nextIsMobile = pointerMediaQuery.matches || window.innerWidth <= 768 if (nextIsMobile === isMobile) return isMobile = nextIsMobile maxCards = isMobile ? 120 : 180 spawnInterval = isMobile ? 700 : 400 document.body.classList.toggle('is-mobile', isMobile) scheduleNextSpawn() } function handleBoardClick(event) { const control = event.target.closest('.control') if (!control) return const card = control.closest('.card') if (!card || !board.contains(card)) return event.preventDefault() if (control.classList.contains('close')) { closeCard(card) } else if (control.classList.contains('minimize')) { minimizeCard(card) } else if (control.classList.contains('maximize')) { toggleMaximize(card) } } function handleBoardPointerDown(event) { const card = event.target.closest('.card') if (!card || !board.contains(card)) return const control = event.target.closest('.control') const header = event.target.closest('.card-header') const pointerType = event.pointerType || 'mouse' const isPrimaryPointer = event.isPrimary !== false if ( header && !control && pointerType !== 'touch' && isPrimaryPointer ) { startDrag(event, card) return } bringToFront(card) } function handleBoardDoubleClick(event) { const header = event.target.closest('.card-header') if (!header || event.target.closest('.control')) return const card = header.closest('.card') if (!card || !board.contains(card)) return toggleMaximize(card) } board.addEventListener('click', handleBoardClick) board.addEventListener('pointerdown', handleBoardPointerDown) board.addEventListener('dblclick', handleBoardDoubleClick) function closeCard(card) { const state = cardStates.get(card) if (!state || state.closing) return if (card === activeMaximizedCard) { activeMaximizedCard = null updateBodyMaximizedState() } state.closing = true state.scale = 0.1 card.style.opacity = '0' applyTransform(card, state) const handleTransitionEnd = event => { if (event.propertyName === 'opacity') { card.removeEventListener('transitionend', handleTransitionEnd) card.remove() } } card.addEventListener('transitionend', handleTransitionEnd) } function minimizeCard(card) { const state = cardStates.get(card) if (!state || state.closing) return // 最小化动画:缩小并淡出到底部,结束时移除节点释放内存 const runMinimize = () => { state.closing = true bringToFront(card) const bottom = Math.max(window.innerHeight - 24, 0) const targetLeft = clamp( state.left, 16, Math.max(window.innerWidth - card.offsetWidth - 16, 16) ) state.left = targetLeft state.top = bottom state.scale = 0.1 state.angle = 0 card.style.left = `${targetLeft}px` card.style.top = `${bottom}px` card.style.opacity = '0.35' applyTransform(card, state) const handleTransitionEnd = event => { if (event.propertyName === 'transform') { card.removeEventListener('transitionend', handleTransitionEnd) card.remove() } } card.addEventListener('transitionend', handleTransitionEnd) } if (state.maximized) { restoreFromMaximize(card, state) requestAnimationFrame(() => { requestAnimationFrame(runMinimize) }) return } runMinimize() } function toggleMaximize(card) { const state = cardStates.get(card) if (!state || state.closing) return if (state.maximized) { restoreFromMaximize(card, state) } else { maximizeCard(card, state) } } function maximizeCard(card, state) { if (activeMaximizedCard && activeMaximizedCard !== card) { const activeState = cardStates.get(activeMaximizedCard) if (activeState) { restoreFromMaximize(activeMaximizedCard, activeState) } } state.beforeMaximize = { left: state.left, top: state.top, scale: state.scale ?? 1, angle: state.angle ?? 0, width: card.offsetWidth, height: card.offsetHeight, inlinePosition: card.style.position } card.classList.add('maximized') card.style.position = 'fixed' card.style.left = '0px' card.style.top = '0px' card.style.width = '100vw' card.style.height = '100dvh' card.style.borderRadius = '0' state.left = 0 state.top = 0 state.scale = 1 state.angle = 0 applyTransform(card, state) activeMaximizedCard = card bringToFront(card) state.maximized = true updateBodyMaximizedState() } function restoreFromMaximize(card, state) { const previous = state.beforeMaximize if (!previous) return card.classList.remove('maximized') card.style.position = previous.inlinePosition || 'absolute' card.style.left = `${previous.left}px` card.style.top = `${previous.top}px` card.style.width = `${previous.width}px` card.style.height = `${previous.height}px` card.style.borderRadius = '12px' state.left = previous.left state.top = previous.top state.scale = previous.scale ?? 1 state.angle = previous.angle ?? state.angle ?? 0 applyTransform(card, state) state.maximized = false if (activeMaximizedCard === card) { activeMaximizedCard = null updateBodyMaximizedState() } bringToFront(card) setTimeout(() => { if (!state.maximized) { card.style.width = '' card.style.height = '' card.style.borderRadius = '' if (previous.inlinePosition) { card.style.position = previous.inlinePosition } else { card.style.position = '' } state.beforeMaximize = null } }, 360) } function startDrag(event, card) { const control = event.target.closest('.control') if (control) return const state = cardStates.get(card) if (!state || state.closing || state.maximized) return // 鼠标拖拽使用 rAF 节流,避免频繁触发布局计算 event.preventDefault() bringToFront(card) const header = card.querySelector('.card-header') card.classList.add('dragging') header.classList.add('dragging') state.dragging = true state.dragOffsetX = event.clientX - state.left state.dragOffsetY = event.clientY - state.top let dragFrame = null let pendingLeft = state.left let pendingTop = state.top const commitDrag = () => { dragFrame = null const maxLeft = Math.max(window.innerWidth - card.offsetWidth, 0) const maxTop = Math.max(window.innerHeight - card.offsetHeight, 0) state.left = clamp(pendingLeft, -card.offsetWidth * 0.4, maxLeft) state.top = clamp(pendingTop, -card.offsetHeight * 0.4, maxTop) card.style.left = `${state.left}px` card.style.top = `${state.top}px` } const handlePointerMove = moveEvent => { if (!state.dragging) return pendingLeft = moveEvent.clientX - state.dragOffsetX pendingTop = moveEvent.clientY - state.dragOffsetY if (dragFrame === null) { dragFrame = requestAnimationFrame(commitDrag) } } const handlePointerUp = () => { state.dragging = false card.classList.remove('dragging') header.classList.remove('dragging') if (dragFrame !== null) { cancelAnimationFrame(dragFrame) commitDrag() } document.removeEventListener('pointermove', handlePointerMove) document.removeEventListener('pointerup', handlePointerUp) } document.addEventListener('pointermove', handlePointerMove) document.addEventListener('pointerup', handlePointerUp) } function createCard() { const card = document.createElement('div') card.className = 'card' const color = randomFrom(colors) const angleRange = isMobile ? 6 : 10 const angle = (Math.random() - 0.5) * angleRange const entryScale = isMobile ? 0.8 : 0.65 const cardWidth = isMobile ? 180 : 220 const cardHeight = isMobile ? 130 : 140 const horizontalMargin = isMobile ? 12 : 16 const verticalMargin = isMobile ? 12 : 20 const left = horizontalMargin + Math.random() * Math.max(window.innerWidth - cardWidth - horizontalMargin * 2, 0) const top = verticalMargin + Math.random() * Math.max(window.innerHeight - cardHeight - verticalMargin * 2, 0) card.style.background = color card.style.left = `${left}px` card.style.top = `${top}px` card.style.opacity = '0' if (activeMaximizedCard && zIndexCursor >= MAXIMIZED_LAYER - 2) { zIndexCursor = MAXIMIZED_LAYER - 2 } card.style.zIndex = ++zIndexCursor card.innerHTML = ` <div class="card-header"> <div class="window-controls"> <button class="control close" type="button" aria-label="关闭"></button> <button class="control minimize" type="button" aria-label="最小化"></button> <button class="control maximize" type="button" aria-label="最大化"></button> </div> <div class="card-title">温馨提示</div> </div> <div class="card-body">${randomFrom(messages)}</div> ` const state = { angle, scale: entryScale, left, top, maximized: false, closing: false } cardStates.set(card, state) applyTransform(card, state) board.appendChild(card) requestAnimationFrame(() => { requestAnimationFrame(() => { state.scale = 1 applyTransform(card, state) card.style.opacity = '1' }) }) if (board.children.length > maxCards) { const oldest = board.firstElementChild if (oldest && oldest !== card) { oldest.remove() } } } for (let i = 0; i < initialCardCount; i++) { setTimeout(createCard, i * (isMobile ? 60 : 40)) } scheduleNextSpawn() document.addEventListener('visibilitychange', () => { if (!document.hidden) { scheduleNextSpawn() } }) if (typeof pointerMediaQuery.addEventListener === 'function') { pointerMediaQuery.addEventListener('change', syncMobileMode) } else if (typeof pointerMediaQuery.addListener === 'function') { pointerMediaQuery.addListener(syncMobileMode) } window.addEventListener('resize', syncMobileMode) </script> </body> </html> -
为什么拆卡直播如此流行? 拆卡直播的流行源于多重因素的综合作用,包括心理吸引力、经济激励、社交需求以及行业特性。以下基于相关信息和当前环境(2025年9月)进行专业分析: 心理因素:不确定性带来的刺激和情绪价值 拆卡直播利用人类对不确定性的天然偏好,创造出类似博彩的“以小博大”机制。消费者支付小额费用(如2元、5元或10元每包)参与拆卡,期望抽中稀有卡牌(如限量版或高价值卡片),这能触发多巴胺释放,提供即时快感和成就感。例如,稀有卡在二级市场可能溢价数十倍,强化了“运气致富”的幻想。 情绪价值是关键驱动力:直播间的互动氛围(如主播的夸张话术、观众围观)增强了代入感,让用户感到参与一场集体游戏。青少年群体尤其易受影响,因为这满足了他们在学业压力下的心理缺口(如寻求认同、社交归属),但这也可能导致成瘾行为,扭曲对努力与运气的认知。 经济因素:低门槛入局与潜在高回报 拆卡直播的运营成本极低,仅需基础设备(如桌子、剪刀、卡牌库存),无需主播露脸或复杂技术。这使得大量个体或小团队能快速入局,2023-2024年期间曾被视为“蓝海”生意,头部主播单场销售额可达数万元。 然而,行业已陷入内卷:市场竞争加剧后,直播间通过设置复杂玩法(如“叠叠乐”或“必中包”)吸引用户,但实际利润被价格战侵蚀。主播常人为操控爆率(如预选稀有卡牌,优先分配给高消费用户),制造虚假“欧气”现象,这虽短期提升销量,却破坏了公平性,导致消费者损失(如投入3万元仅获普卡)。截至2025年9月,行业正经历信任危机,批量直播间倒闭,幸存者依赖供应链优势或创新玩法维持。 社会与行业因素:社交需求与监管缺失 拆卡直播构建了虚拟社交圈,用户通过分享抽卡结果或收集成套卡牌获得群体认同,这尤其吸引青少年和二次元文化爱好者。平台算法(如抖音、小红书的热度上升)进一步推高了曝光度,形成“跟风”效应。 但行业监管滞后:主播的诱导行为(如虚假宣传爆率)涉嫌擦边赌博,违反公平交易原则。未成年人保护机制薄弱(如身份验证缺失),导致退费维权困难。当前趋势显示,监管压力正在加大(如要求公示概率、设置消费上限),但乱象仍存,需行业自律和政策完善。 总结与建议 拆卡直播的流行是娱乐消费与人性博弈的结合,其吸引力源于低成本、高刺激和社交属性,但内在风险(如操控爆率、成瘾问题)已引发内卷和信任危机。截至2025年9月,行业热度有所降温,但头部直播间仍能通过创新维持。消费者应理性参与,避免过度投入;监管方需强化规则(如概率透明化),以促进行业健康发展。 -
喜欢网络简报 2025年8月9日 星期六 1. 证监会:将继续严把发行上市入口关,不会出现大规模扩容的情况。 2. 工行、农行、建行等多家银行响应消费贷贴息,贴息标准多在1.5%左右。 3. 嫦娥六号月球样品最新研究成果发布:揭示月球背面月幔超还原状态。 4. 北京优化限购:符合条件家庭购买五环外住房不限套数。 5. 上海推女职工产假社保补贴政策:补贴用人单位50%,8月底可申请。 6. 山东养老服务消费补贴来了:不限户籍,60岁以上中重度失能可申领。 7. 2025世界人形机器人运动会下周启幕,将有280支队伍参赛。 8. 日媒:日本首相石破茂再次表明将继续留任。 9. 柬埔寨首相:已正式提名特朗普获诺贝尔和平奖。 10. 美国称悬赏5000万美元缉拿委内瑞拉总统马杜罗。 11. 以色列已批准占领加沙城计划,拟建立加沙新民事政府。 12.证监会严肃查处贵州辖区深交所主板上市公司*ST高鸿严重财务造假案件,对大唐高鸿罚款1.6亿元。 13.快手上线独立外卖入口,外卖商品二季度支付用户数环比增长超3倍。 【心语】无论遇见何种风景,都请欣然接受。用随遇而安的态度,过顺其自然的生活。 -
新加坡和马来西亚开展TikTok Shop 跨境电商业务需要外网和报白吗? 如果您计划在 新加坡和马来西亚 开展 TikTok Shop 跨境电商业务,以下是关键要点和操作指南: 1. TikTok Shop 跨境入驻基础要求 (1)是否需要外网(VPN)? 中国大陆商家: 由于TikTok Shop在部分国家(如东南亚)的卖家中心可能限制中国IP访问,可能需要VPN(如切换至新加坡/马来西亚IP)才能注册和运营。 风险提示:TikTok官方禁止使用VPN绕过地区限制,可能导致封号,建议用合规方式入驻(如通过跨境代理或本地公司)。 新加坡/马来西亚本地商家: 直接使用本地网络即可,无需VPN。 (2)是否需要“报白”? TikTok Shop的“报白”(白名单)通常指 特殊类目准入,但新马市场规则略有不同: 普通类目(如服饰、家居、美妆):无需报白,直接上架。 限制类目(如食品、保健品、电子烟): 需提交 相关认证(如马来西亚的MDA、新加坡的HSA许可)。 部分类目需申请 平台定向邀约(类似“报白”)。 2. 新加坡 & 马来西亚 TikTok Shop 入驻方式 (1)跨境卖家(无本地公司) 适用平台:TikTok Shop 跨境业务(如从中国发货至新马)。 要求: 中国大陆或香港公司营业执照。 跨境电商经验(如亚马逊、Shopee店铺流水)。 绑定 跨境收款账户(如Payoneer、万里汇)。 是否需要VPN: 中国大陆IP可能无法直接访问TikTok Seller Center(需切换至海外IP)。 (2)本地卖家(新加坡/马来西亚公司) 优势:无需VPN,可直接入驻,流量扶持更多。 要求: 本地公司注册(如新加坡ACRA或马来西亚SSM)。 本地银行账户(如DBS、Maybank)。 本地仓库或合作物流(如Ninja Van、J&T)。 3. 关键操作步骤 (1)注册TikTok Shop卖家账号 访问官网: 新加坡:https://seller.tiktokglobalshop.com 马来西亚:https://seller.tiktokglobalshop.com 选择入驻类型:跨境卖家 or 本地卖家。 提交资料:营业执照、法人身份证、店铺流水等。 (2)物流与支付 跨境模式: 使用TikTok官方物流(如TikTok Shipping)或第三方(如极兔国际)。 本地模式: 本地仓发货(如Lazada FBL、Shopee SLS)。 收款:绑定Payoneer、连连支付或本地银行账户。 (3)类目审核(如需“报白”) 如涉及特殊商品(如保健品、3C),联系TikTok客服或客户经理申请类目权限。 4. 风险与合规 VPN风险:TikTok可能检测IP变动,建议用固定海外IP或本地公司运营。 税务合规: 新加坡:需注册GST(如年销售额超100万新元)。 马来西亚:需申请SST(如年销售额超50万令吉)。 内容合规:避免宗教、政治敏感内容,符合新马广告法。 5. 建议方案 | 情况 | 推荐方式 | |----------|-------------| | 中国大陆卖家,无本地公司 | 用跨境模式+海外IP(谨慎VPN) | | 新加坡/马来西亚本地公司 | 直接本地入驻,无需VPN | | 销售限制类目(如食品) | 提前申请类目白名单 | 总结 是否需要VPN:中国大陆卖家可能需要,本地商家不用。 是否需要报白:普通类目不用,特殊类目需申请。 最佳路径:如有条件,建议注册新加坡/马来西亚公司,避免IP风险。 如果需要更具体的入驻流程或类目报白指导,可以提供您的业务模式(跨境/本地),我可以进一步细化方案! -
一个花里胡哨的确认按钮(可复制源代码) 640.gif图片 真的很不错! <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8" /> <title>一个花里胡哨的确认按钮</title> <style>.button{ --background: #1e2235; --color: #f6f8ff; --shadow: rgba(0, 9, 61, 0.24); --cannon-dark: #a6accd; --cannon-light: #f6f8ff; --cannon-shadow: rgba(13, 15, 24, 0.9); --confetti-1: #892ab8; --confetti-2: #ea4c89; --confetti-3: #ffff04; --confetti-4: #4af2fd; --z-before: -6; display: block; outline: none; cursor: pointer; position: relative; border: 0; background: none; padding: 9px22px9px16px; line-height: 26px; font-family: inherit; font-weight: 600; font-size: 14px; color: var(--color); -webkit-appearance: none; -webkit-tap-highlight-color: transparent; transition: transform var(--transform-duration, 0.4s); will-change: transform; transform-style: preserve-3d; transform: perspective(440px) rotateX(calc(var(--rx, 0) * 1deg)) rotateY(calc(var(--ry, 0) * 1deg)) translateZ(0);} .button:hover{ --transform-duration: 0.16s;} .button.success{ --confetti-scale: 0; --stroke-dashoffset: 15;} .button:before{ content: ""; position: absolute; left: 0; top: 0; right: 0; bottom: 0; border-radius: 12px; transform: translateZ(calc(var(--z-before) * 1px)); background: var(--background); box-shadow: 04px8pxvar(--shadow);} .button.icon, .buttonspan{ display: inline-block; vertical-align: top; position: relative; z-index: 1;} .button.icon{ --z: 2px; width: 24px; height: 14px; margin: 8px16px00; transform: translate( calc(var(--icon-x, 0) * 1px), calc(var(--icon-y, 0) * 1px) ) translateZ(2px);} .button.icon.confetti{ position: absolute; left: 17px; bottom: 9px;} .button.icon.confettisvg{ width: 18px; height: 16px; display: block; stroke-width: 1px; fill: none; stroke-linejoin: round; stroke-linecap: round;} .button.icon.confettisvg *{ transition: stroke-dashoffset 0.2s; stroke-dasharray: 1520; stroke-dashoffset: var(--stroke-dashoffset, 0); stroke: var(--stroke-all, var(--stroke, var(--confetti-2)));} .button.icon.confettisvg *:nth-child(2){ --stroke: var(--confetti-3);} .button.icon.confettisvg *:nth-child(3){ --stroke: var(--confetti-1);} .button.icon.confetti.emitter{ position: absolute; left: 4px; bottom: 4px; pointer-events: none;} .button.icon.confetti.emitterdiv{ width: 4px; height: 4px; margin: -2px00 -2px; border-radius: 1px; position: absolute; left: 0; top: 0; transform-style: preserve-3d; background: var(--confetti-all, var(--b, none));} .button.icon.confettii{ width: 4px; height: 4px; display: block; transform: scale(var(--confetti-scale, 0.5)); position: absolute; transition: transform 0.25s; left: var(--left, -1px); top: var(--top, 3px); border-radius: var(--border-radius, 1px); background: var(--confetti-background, var(--confetti-3));} .button.icon.confettii:nth-child(2){ --left: 9px; --top: -1px; --border-radius: 2px; --confetti-background: var(--confetti-4);} .button.icon.confettii:nth-child(3){ --left: 5px; --top: 3px; --confetti-background: var(--confetti-1);} .button.icon.confettii:nth-child(4){ --left: 10px; --top: 14px; --confetti-background: var(--confetti-2);} .button.icon.confettii:nth-child(5){ --left: 9px; --top: 7px; --confetti-background: var(--confetti-4);} .button.icon.confettii:nth-child(6){ --left: 6px; --top: 8px; --border-radius: 2px; --confetti-background: var(--confetti-2);} .button.icon.cannon{ position: relative; width: 24px; height: 14px; transform: translate(0, 3px) rotate(-45deg); filter: drop-shadow(-2px2px2px var(--cannon-shadow));} .button.icon.cannon:before, .button.icon.cannon:after{ content: ""; display: block; height: 14px;} .button.icon.cannon:before{ background: linear-gradient( var(--cannon-dark), var(--cannon-light) 50%, var(--cannon-dark) ); width: 100%; -webkit-clip-path: polygon(25px -1px, 052%, 25px15px); clip-path: polygon(25px -1px, 052%, 25px15px);} .button.icon.cannon:after{ width: 6px; position: absolute; right: -3px; top: 0; border-radius: 50%; box-shadow: inset 0000.5pxvar(--cannon-light); background: linear-gradient( 90deg, var(--cannon-dark), var(--cannon-light) );} .button.white{ --background: #fff; --color: #1e2235; --border: #e1e6f9; --shadow: none; --cannon-dark: #103fc5; --cannon-light: #275efe; --cannon-shadow: rgba(0, 9, 61, 0.2);} .button.white:before{ box-shadow: inset 0001pxvar(--border);} .button.grey{ --background: #404660; --cannon-shadow: rgba(13, 15, 24, 0.2); --cannon-dark: #d1d6ee; --cannon-light: #ffffff;} html{ box-sizing: border-box; -webkit-font-smoothing: antialiased;} *{ box-sizing: inherit;} *:before, *:after{ box-sizing: inherit;} body{ min-height: 100vh; display: flex; font-family: "Inter", Arial; justify-content: center; align-items: center; background: #f6f8ff;} body.button{ margin: 012px;} body.dribbble{ position: fixed; display: block; right: 20px; bottom: 20px;} body.dribbbleimg{ display: block; height: 28px;} body.twitter{ position: fixed; display: block; right: 64px; bottom: 14px;} body.twittersvg{ width: 32px; height: 32px; fill: #1da1f2;} </style> </head> <body> <button class="button"><div class="icon"><div class="cannon"></div><div class="confetti"><svg viewBox="0 0 18 16"><polyline points="1 10 4 7 4 5 6 1" /><path d="M4,13 C5.33333333,9 7,7 9,7 C11,7 12.3340042,6 13.0020125,4" /><path d="M6,15 C7.83362334,13.6666667 9.83362334,12.6666667 12,12 C14.1663767,11.3333333 15.8330433,9.66666667 17,7" /></svg><i></i><i></i><i></i><i></i><i></i><i></i><div class="emitter"></div></div></div><span>Confirm</span></button> <script src="https://cdn.jsdelivr.net/npm/gsap@3.13.0/dist/gsap.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/gsap@3.13.0/dist/Physics2DPlugin.min.js"></script> <script>document.querySelectorAll(".button").forEach((button)=>{ const bounding=button.getBoundingClientRect(); button.addEventListener("mousemove", (e)=>{ let dy=(e.clientY - bounding.top - bounding.height / 2) / -1; let dx=(e.clientX - bounding.left - bounding.width / 2) / 10; dy=dy >10 ? 10 : dy < -10 ? -10 : dy; dx=dx >4 ? 4 : dx < -4 ? -4 : dx; button.style.setProperty("--rx", dy); button.style.setProperty("--ry", dx);}); button.addEventListener("mouseleave", (e)=>{ button.style.setProperty("--rx", 0); button.style.setProperty("--ry", 0);}); button.addEventListener("click", (e)=>{ button.classList.add("success"); gsap.to(button,{ "--icon-x": -3, "--icon-y": 3, "--z-before": 0, duration: 0.2, onComplete(){ particles(button.querySelector(".emitter"), 100, -4, 6, -80, -50); gsap.to(button,{ "--icon-x": 0, "--icon-y": 0, "--z-before": -6, duration: 1, ease: "elastic.out(1, .5)", onComplete(){ button.classList.remove("success");},});},});});}); function particles(parent, quantity, x, y, minAngle, maxAngle){ let colors=["#FFFF04", "#EA4C89", "#892AB8", "#4AF2FD"]; for (let i=quantity - 1; i >=0; i--){ let angle=gsap.utils.random(minAngle, maxAngle), velocity=gsap.utils.random(70, 140), dot=document.createElement("div"); dot.style.setProperty( "--b", colors[Math.floor(gsap.utils.random(0, 4))] ); parent.appendChild(dot); gsap.set(dot,{ opacity: 0, x: x, y: y, scale: gsap.utils.random(0.4, 0.7),}); gsap .timeline({ onComplete(){ dot.remove();},}) .to( dot, { duration: 0.05, opacity: 1,}, 0 ) .to( dot, { duration: 1.8, rotationX: `-=${gsap.utils.random(720, 1440)}`, rotationZ: `+=${gsap.utils.random(720, 1440)}`, physics2D:{ angle: angle, velocity: velocity, gravity: 120,},}, 0 ) .to( dot, { duration: 1, opacity: 0,}, 0.8 );}} </script> </body> </html> -
程序员财务自由幻觉图鉴 1. "等公司上市就好了" 2016年加入创业公司时,CEO拍着我肩膀说: "现在工资是低了点,但等B轮融资后..." 2018年B轮了,他说:"等C轮估值上去..." 2020年终于上市了——我的期权刚好够买公司纪念品商店里的一个马克杯。 2. 程序员的财富幻想进化史 阶段1:接私活暴富 学生时代觉得"会写代码=印钞机" 第一次接外包:仿今日头条,预算5000块 结局:甲方要加"小小小功能",工时翻三倍 阶段2:等期权变现 每天查公司估值像查股票 学会新词:"行权价"、"锁定期"、"资本利得税" 最后发现税后还不够买厕所一平米 阶段3:加密货币 2017年错过比特币(觉得是骗局) 2021年错过狗狗币(觉得是玩笑) 2023年冲进屎币——终于成功被割 阶段4:知识付费 录了《21天成为全栈工程师》课程 销量主要靠亲朋好友友情支持 算上录制时间,时薪不如麦当劳打工 3. 那些年我们信过的财富神话 传说1:"有个前辈早年加入阿里..." 现实:你加入的是"阿里系"创业公司 区别相当于正版孙悟空和景区cosplay 传说2:"我朋友炒币赚了千万" 现实:他忘了说后来亏了两千万 以及他所谓的"炒币"其实是传销 传说3:"技术大牛都靠接咨询发财" 现实:你接到的咨询都是 "帮我侄子看下毕业设计" "简单做个淘宝那样的网站" 4. 财务自由等级对照表 | 等级 | 描述 | 实现方式 | |------|------|----------| | 幻觉级 | 植发自由 | 年终奖够做一次FUE | | 入门级 | 显卡自由 | 买RTX4090不眨眼 | | 进阶级 | 外卖自由 | 点餐敢选"无需凑单" | | 终极级 | 离职自由 | 存款够骂老板后裸辞 | (大部分人卡在"星巴克自由"和"打车自由"之间) 5. 可乐的破产经验谈 (1) 那些亏钱的骚操作 跟风买矿机(到货时矿难了) 投资朋友"绝对靠谱"的区块链项目(后来他失联了) 炒股(把技术分析学成了玄学) (2) 唯一赚到钱的项目 老实写代码领工资 虽然无聊,但比炒币踏实 (3) 血泪教训 当你听说某个"稳赚"的机会时,已经晚了 程序员最容易犯的错:以为技术好=投资厉害 真实财富密码:有个富二代室友/前同事/同学 6. 当代程序员财富焦虑解决方案 方案1:降低标准 把"财务自由"重新定义为"外卖自由" 发现瞬间就实现了 方案2:转移焦点 关注"时间自由"而不是"财富自由" 比如争取远程工作,省下通勤时间 方案3:创造副业 写技术博客(虽然赚不到钱但能装逼) 做开源项目(虽然倒贴钱但能镀金) 方案4:终极解法 卸载所有财经APP 屏蔽那些"成功上岸"的前同事朋友圈 专注写代码,等35岁被优化时再说 7. 残酷真相 用Python分析了身边100个"财务自由"案例: 80%靠家里拆迁/继承家产 15%是早期加入巨头公司的幸运儿 5%是真正的创业成功者(其中4个已秃) 最终觉悟: 我们这代人注定在"等公司上市"和"等老家拆迁"之间反复横跳。 (所以还是先把今天的代码提交了吧) (完) 下期预告 《程序员年龄焦虑实录:从"少年天才"到"大龄码农"》 那些25岁当CTO和35岁送外卖的故事 技术更新的速度 vs 头发掉落的速度 如何让自己在年轻同事面前不显得像出土文物 (想看扣⌨️,让我看看多少同行开始焦虑了)